JAMES-4025 Drop artifact james-server-jmap-draft
diff --git a/pom.xml b/pom.xml
index 8ba3834..1b5e5d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1564,11 +1564,6 @@
             </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
-                <artifactId>james-server-jmap-draft</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>${james.groupId}</groupId>
                 <artifactId>james-server-jmap-rfc-8621</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/server/pom.xml b/server/pom.xml
index 5bfb6f8..fc24429 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -94,7 +94,6 @@
 
         <module>protocols/fetchmail</module>
         <module>protocols/jmap</module>
-        <module>protocols/jmap-draft</module>
         <module>protocols/jmap-rfc-8621</module>
         <module>protocols/jmap-rfc-8621-integration-tests</module>
         <module>protocols/jwt</module>
diff --git a/server/protocols/jmap-draft/doc/Dockerfile b/server/protocols/jmap-draft/doc/Dockerfile
deleted file mode 100644
index cd71a80..0000000
--- a/server/protocols/jmap-draft/doc/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-FROM iron/python:2-dev
-
-RUN pip install mkdocs
-
-COPY . /doc
-
-RUN find . -name "*.mdwn" | while IFS= read -r f; do mv -v "$f" "$(echo "$f" | sed -e 's/.mdwn$/.md/' - )"; done
-
-RUN mv /doc/specs/spec/apimodel.md /doc/specs/index.md
-
-WORKDIR /doc
-
-RUN mkdocs build --clean
-
-WORKDIR site
-
-EXPOSE 8000
-
-CMD python -m SimpleHTTPServer 8000
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/doc/README.md b/server/protocols/jmap-draft/doc/README.md
deleted file mode 100644
index 99c78b2..0000000
--- a/server/protocols/jmap-draft/doc/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Annotated JMAP documentation
-============================
-
-This directory contains annotated jmap documentation as found on http://jmap.io/.
-
-Annotations aim at tracking implementation progress in James project.
-
-You can read it as an html site using the provided Dockerfile.
-

-$ docker build --tag=mkdocs .
-$ docker run --rm -p 8000:8000 mkdocs

-
-Then open http://localhost:8000/ in your browser.
diff --git a/server/protocols/jmap-draft/doc/annotation.css b/server/protocols/jmap-draft/doc/annotation.css
deleted file mode 100644
index 70134dd..0000000
--- a/server/protocols/jmap-draft/doc/annotation.css
+++ /dev/null
@@ -1,16 +0,0 @@
-aside {
-  padding: 1em;
-  margin-top: 1.5em;
-  margin-bottom: 1.5em;
-  background: #6F8000;
-  line-height: 1.6;
-  font-family: 'FontAwesome';
-}
-aside:before {
-  padding-right: 0.5em;
-  font-size: 14px;
-}
-aside.warning { background: #ac5c64; }
-aside.warning:before { content: "\f071"; }
-aside.notice { background: #67875C; }
-aside.notice:before { content: "\f06a"; }
diff --git a/server/protocols/jmap-draft/doc/mkdocs.yml b/server/protocols/jmap-draft/doc/mkdocs.yml
deleted file mode 100644
index 0e5c77f..0000000
--- a/server/protocols/jmap-draft/doc/mkdocs.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-docs_dir: specs
-site_name: JMAP specifications with annotations from apache/james contributors
-theme: readthedocs
-use_directory_urls: false
-extra_css:
-  - annotations.css
diff --git a/server/protocols/jmap-draft/doc/specs/LICENSE.md b/server/protocols/jmap-draft/doc/specs/LICENSE.md
deleted file mode 100644
index bbe7cbf..0000000
--- a/server/protocols/jmap-draft/doc/specs/LICENSE.md
+++ /dev/null
@@ -1,190 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-   
-   Copyright 2016 FastMail Pty Ltd
-
-   Licensed 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.
diff --git a/server/protocols/jmap-draft/doc/specs/README b/server/protocols/jmap-draft/doc/specs/README
deleted file mode 100644
index 0ef3d2c..0000000
--- a/server/protocols/jmap-draft/doc/specs/README
+++ /dev/null
@@ -1,8 +0,0 @@
-JSON Mail Access Protocol Specification (JMAP)
-----------------------------------------------
-
-This repository contains the specification for JMAP, a new JSON-based API for synchronising a mail client with a mail server. It is intended as a replacement for IMAP. The specification is based on the API currently used by the FastMail (https://www.fastmail.com) web app.  It aims to be compatible with the IMAP data model, so that it can be easily implemented on a server that currently supports IMAP, but allows for reduced data usage and more efficient synchronisation, bundling of requests for latency mitigation and is generally much easier to work with than IMAP.
-
-The pretty HTML version of the spec is hosted at http://jmap.io.
-
-Want to get involved? Join the mailing list at https://groups.google.com/forum/#!forum/jmap-discuss. Feedback is welcome: send your thoughts or comments on anything that is imprecise, incomplete, or could simply be done better in another way. Discussion is preferred prior to pull requests, except in the case of minor typos etc.!
diff --git a/server/protocols/jmap-draft/doc/specs/client-guide/jmap-client-guide.mdwn b/server/protocols/jmap-draft/doc/specs/client-guide/jmap-client-guide.mdwn
deleted file mode 100644
index c61c24a..0000000
--- a/server/protocols/jmap-draft/doc/specs/client-guide/jmap-client-guide.mdwn
+++ /dev/null
@@ -1,653 +0,0 @@
-Guide for client developers
-===========================
-
-This guide shows how you can use JMAP to efficiently minimise bandwidth usage and round trips whilst still giving the user a complete view of their mail. This is particularly relevant to clients on mobile devices, where there is not the space to store a complete cache of all messages, or for web clients where you often do not have a permanent cache at all and must efficiently start from scratch each time the app is loaded.
-
-A single login may have access to multiple accounts. In this guide I'm going to ignore this and just use the primary account, which I'm going to presume has full read-write mail access.
-
-## Cold boot
-
-When a user first logs in and you have no data cached for them, first call:
-
-    [
-        [ "getMailboxes", {}, "#0" ]
-    ]
-
-This will fetch the list of mailboxes (folders or labels) for the user, and the permissions and roles for each. Here's an example of the response you might receive:
-
-    [
-        [ "mailboxes", {
-            accountId: 'user@example.com',
-            state: "m123456789",
-            list: [
-                {
-                    id: "mailbox1",
-                    name: "Inbox",
-                    parentId: null,
-                    role: "inbox",
-                    mustBeOnlyMailbox: false,
-                    mayAddMessages: true,
-                    mayRemoveMessages: true,
-                    mayCreateChild: false,
-                    mayRenameMailbox: true,
-                    mayDeleteMailbox: false,
-                    totalMessages: 1424,
-                    unreadMessages: 3,
-                    totalThreads: 1213,
-                    unreadThreads: 2
-                },
-                {
-                    id: "mailbox2",
-                    name: "Sent",
-                    parentId: null,
-                    role: "sent",
-                    mustBeOnlyMailbox: false,
-                    mayAddMessages: true,
-                    mayRemoveMessages: true,
-                    mayCreateChild: false,
-                    mayRenameMailbox: true,
-                    mayDeleteMailbox: false,
-                    totalMessages: 41,
-                    unreadMessages: 0,
-                    totalThreads: 32,
-                    unreadThreads: 2
-                },
-                {
-                    id: "mailbox3",
-                    name: "Trash",
-                    parentId: null,
-                    role: "trash",
-                    mustBeOnlyMailbox: true,
-                    mayAddMessages: true,
-                    mayRemoveMessages: true,
-                    mayCreateChild: false,
-                    mayRenameMailbox: true,
-                    mayDeleteMailbox: false,
-                    totalMessages: 3,
-                    unreadMessages: 0,
-                    totalThreads: 2,
-                    unreadThreads: 0
-                },
-                {
-                    id: "mailbox4",
-                    name: "Awaiting Reply",
-                    parentId: "mailbox2",
-                    role: null,
-                    mustBeOnlyMailbox: false,
-                    mayAddMessages: true,
-                    mayRemoveMessages: true,
-                    mayCreateChild: true,
-                    mayRenameMailbox: true,
-                    mayDeleteMailbox: true,
-                    totalMessages: 0,
-                    unreadMessages: 0,
-                    totalThreads: 0,
-                    unreadThreads: 0
-                }
-            ],
-            notFound: null
-        }, "#0"]
-    ]
-
-In this (simple) example, the user has four mailboxes. Three at the top level (Inbox, Sent and Trash) and one submailbox of Sent called "Awaiting Reply". The first 3 have `role` attributes, so they are to be used for the designated system roles (these are much as you would expect, but see the spec for full details). Note, you should always use the `role` attribute, as names may be localised (or even different between different servers with the same language)!
-
-The Inbox, Sent and Awaiting Reply mailboxes all have `mustBeOnlyMailbox:false`. The Trash mailbox on the other hand has `mustBeOnlyMailbox:true`. This means that a message in the trash may not be in any other mailbox, and vice versa. However, a message belonging to the Sent mailbox could also be in, for example, the Awaiting Reply mailbox.
-
-The Sent mailbox has no unread messages, but 2 unread threads. This is not an error! A thread is considered unread if any of the messages in it are unread, even if those messages are actually in a different mailbox. In this case, the 3 unread messages in the Inbox must all be replies to messages in the Sent mailbox. Email clients will probably want to hide unread thread counts for mailboxes with a role of "sent" or "archive".
-
-Presuming the client defaults to showing the `role=inbox` mailbox, we now get the list of messages at the top of the mailbox, and the data for each one that we need to show it in the interface.
-
-    [
-        [ "getMessageList", {
-            filter: {
-                inMailboxes: [ "mailbox1" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: true,
-            position: 0,
-            limit: 10,
-            fetchThreads: true,
-            fetchMessages: true,
-            fetchMessageProperties: [
-                "threadId",
-                "mailboxId",
-                "isUnread",
-                "isFlagged",
-                "isAnswered",
-                "isDraft",
-                "hasAttachment",
-                "from",
-                "to",
-                "subject",
-                "date",
-                "preview"
-            ],
-            fetchSearchSnippets: false
-        }, "call1"]
-    ]
-
-This might return the following:
-
-    [
-        [ "messageList", {
-            accountId: 'user@example.com',
-            filter: {
-                inMailboxes: [ "mailbox1" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: true,
-            state: "m8231u341",
-            canCalculateUpdates: true,
-            position: 0,
-            total: 1213,
-            messageIds: [
-                "fm1u314",
-                "fm1u312",
-                "fm1u298",
-                "fm1u286",
-                "fm1u265",
-                "fm1u254",
-                "fm1u241",
-                "fm1u211",
-                "fm1u109",
-                "fm1u3"
-            ],
-            threadIds: [
-                "4f512aafed75e7fb",
-                "fed75e7fb4f512aa",
-                "75e7fb4f512aafed",
-                "512aafed75e7fb4f",
-                "fb4f512aafed75e7",
-                "2aafed75e7fb4f51",
-                "afed75fb4f512ae7",
-                "e7fb4f512aafed75",
-                "ed75e74f51fb2aaf",
-                "b4f5ed75e712aaff"
-            ]
-        }, "call1" ],
-        [ "threads", {
-            accountId: 'user@example.com',
-            state: "mc1264092",
-            list: [{
-                id: "4f512aafed75e7fb",
-                messageIds: [ "fm1u314" ]
-            }, {
-                id: "fed75e7fb4f512aa",
-                messageIds: [ "fm1u312", "fm2u12", "fm1u304" ]
-            },
-                … 8 more thread objects, omitted for brevity …
-            ],
-            notFound: null
-        }],
-        [ "messages", {
-            accountId: 'user@example.com',
-            state: "m815034",
-            list: [{
-                "id": "fm1u314",
-                "threadId": "4f512aafed75e7fb",
-                "mailboxIds": [ "mailbox1" ],
-                "isUnread": true,
-                "isFlagged": false,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": false,
-                "from": [
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" }
-                ],
-                "subject": "Camping trip",
-                "date": "2014-07-24T15:04:51Z",
-                "preview": "Hey Joe. Fancy a trip out west next week? I hea…"
-            },
-            {
-                "id": "fm1u312",
-                "threadId": "fed75e7fb4f512aa",
-                "mailboxIds": [ "mailbox1" ],
-                "isUnread": false,
-                "isFlagged": true,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": true,
-                "from": [
-                    { name: "James Connor", email: "jamesconnorwork@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" },
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "subject": "Re: I need the swallow velocity report ASAP",
-                "date": "2014-07-24T12:01:43Z",
-                "preview": "Come on you guys. How long can it take to do a simple scientif…"
-            },
-            {
-                "id": "fm2u12",
-                "threadId": "fed75e7fb4f512aa",
-                "mailboxIds": [ "mailbox2" ],
-                "isUnread": false,
-                "isFlagged": false,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": false,
-                "from": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "James Connor", email: "jamesconnorwork@fastmail.fm" },
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "subject": "Re: I need the swallow velocity report ASAP",
-                "date": "2014-07-24T11:32:15Z",
-                "preview": "It's on its way. Jane."
-            },
-                … more message objects, omitted for brevity …
-            ],
-            notFound: null
-        }, "call1" ]
-    ]
-
-We now have the header information for all the messages in the top 10 threads in the Inbox, plus a full list of mailboxes and their unread counts. Providing the screen cannot fit more than 10 messages without scrolling, this is sufficient to display a standard mail interface as though **all data was loaded**, even though we have only made 2 round trips to the server, and transferred minimal data. (Obviously, you can request more than 10 in the request if you need more to show the initial screen).
-
-## Paging in data as the interface is navigated
-
-In our example Inbox, there are 1213 threads, but so far we have only loaded in data for the first 10. As the user scrolls down, we need to page in the data for the section of the mailbox that becomes visible (and indeed, to avoid the user having to wait, it's advisable to preload a little way ahead too). This is just another call to `getMessageList`, as in the cold boot example, but with the `position` property changed to the index for the section required.
-
-Similarly, if we switch mailboxes, or want to do a search, we can use the same call as well. However, remember in JMAP the same message may appear in multiple mailboxes, so to avoid downloading the same data multiple times, it's advisable to just fetch the message list without also getting the message or thread objects (except in certain situations, such as the very first request; the exact heuristics for deciding between the two can be made arbitrarily clever and complex).
-
-    [
-        [ "getMessageList", {
-            filter: {
-                inMailboxes: [ "mailbox2" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: true,
-            position: 11,
-            limit: 10,
-            fetchThreads: false,
-            fetchMessages: false,
-            fetchMessageProperties: null,
-            fetchSearchSnippets: false
-        }, "call1"]
-    ]
-
-After the list has been returned, a second request can be made directly to `getThreads` with the thread ids (or to `getMessages` with the message ids) to fetch the ones you are missing.
-
-## Opening a thread
-
-So far we have only fetched the minimal amount of information we need to display a mailbox entry for a message or thread. When opening a thread from the mailbox, we now need to fetch the rest of the message details. Exactly what you wish to fetch depends on what information your client displays. Suppose we open the 2nd thread in the example Inbox above. I'm presuming we already know the list of message ids in the thread from the data received when we fetched the mailbox.
-
-    [
-        [ "getMessages", {
-            ids: [ "fm1u312", "fm2u12", "fm1u304" ],
-            properties: [
-                "threadId",
-                "mailboxIds",
-                "isUnread",
-                "isFlagged",
-                "isAnswered",
-                "isDraft",
-                "hasAttachment",
-                "from",
-                "to",
-                "cc",
-                "bcc",
-                "replyTo",
-                "subject",
-                "date",
-                "size",
-                "body",
-                "attachments",
-                "attachedMessages"
-            ]
-        }]
-    ]
-
-Alternatively, a client may want to just request the "rawUrl" property, then download the original RFC2822 message from the URL returned and parse it in the client.
-
-## Staying in sync
-
-Suppose a new message arrives, or the user performs some actions on their messages using a different client. A push notification comes in to indicate the state has changed on the server, or you get a response back to a method call with a different state string to the previous call. We now need efficiently work out exactly what has changed so that we can update the model in the client and keep it in sync, without throwing away all of our data and starting again.
-
-To efficiently stay in sync, we can call:
-
-    [
-        [ "getMailboxUpdates", {
-            sinceState: "m123456789",
-            fetchRecords: true,
-            fetchRecordProperties: null
-        }, "call1" ],
-        [ "getMessageListUpdates", {
-            filter: {
-                inMailboxes: [ "mailbox1" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: true,
-            sinceState: "m8231u341",
-            uptoMessageId: "fm1u3",
-            maxChanges: 100
-        }, "call2" ],
-        [ "getMessageUpdates", {
-            sinceState: "m815034"
-            maxChanges: 30,
-            fetchRecords: true,
-            fetchRecordProperties: [
-                "threadId",
-                "mailboxIds",
-                "isUnread",
-                "isFlagged",
-                "isAnswered",
-                "isDraft",
-                "hasAttachment",
-                "labels",
-                "from",
-                "to",
-                "subject",
-                "date",
-                "preview"
-            ]
-        }, "call3" ],
-        [ "getThreadUpdates", {
-            sinceState: "mc1264092"
-            maxChanges: 20,
-            fetchRecords: true
-        }, "call4" ]
-    ]
-
-This is a good example of multiple method calls combined into a single request. Let's have a look at what they are doing.
-
-1. `getMailboxUpdates`: This will return the ids of any mailboxes that have been created or modified (e.g. renamed, or have changed counts). It will also return the ids of any mailboxes that have been deleted. Because we set `fetchMailboxes == true`, any new or changed mailboxes will be fetched. If the server supports the `onlyCountsChanged` property of the mailboxes response, then we will only receive the counts properties for the changed mailboxes if that is the only change (the common case).
-2. `getMessageListUpdates`: All our message lists may have changed, so we must mark all of them as needing a refresh, and immediately refresh the one that is currently selected in the client (here, presuming the Inbox). We'll look at the response to this and how it is used to update the client cache below.
-3. `getMessageUpdates`: Gets the list of ids for all messages which have been added or modified, plus the list of messages which have been deleted. We have also specified `fetchMessages` and `fetchMessageProperties` arguments here to request the standard header information we need to display a message in a mailbox for any new/changed messages. This includes all mutable properties of a message, so is sufficient to bring any changed messages up to date.
-4. `getThreadUpdates`: Gets the list of threads which have had messages added or removed from the thread. We also fetch the `Thread` object for those threads that have changed.
-
-In each case, the `sinceState` argument comes from the response to our previous calls to get the records of that type.
-
-### Handling a standard response
-
-In the common case, not many changes will have occurred since we last synced with the server, and the data returned from this request will be sufficient to fully bring the client into sync with the server.
-
-The `maxChanges` argument prevents methods from returning huge volumes of data in the case where a large number of changes have been made. If we get a `tooManyChanges` error in response to one of our method calls then we will then have to do more work to get back in sync, as detailed below.
-
-Let's look at a common case example, where two new messages have been delivered to the Inbox and one other existing message has been marked as read. We might get back a response something like:
-
-    [
-        [ "mailboxUpdates", {
-            accountId: 'user@example.com',
-            oldState: "m123456789",
-            newState: "m123471231",
-            changed: [ "mailbox1" ],
-            removed: [],
-            onlyCountsChanged: true
-        }, "call1" ],
-        [ "mailboxes", {
-            accountId: 'user@example.com',
-            state: "m123471231"
-            list: [{
-                totalMessages: 1426,
-                unreadMessages: 4,
-                totalThreads: 1214,
-                unreadThreads: 3
-            }],
-            notFound: null
-        }, "call1" ],
-        [ "messageListUpdates", {
-            accountId: 'user@example.com',
-            filter: {
-                inMailboxes: [ "mailbox1" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: true,
-            oldState: "m8231u341",
-            newState: "m8239u342",
-            uptoMessageId: "fm1u3",
-            removed: [{
-                messageId: "fm1u241",
-                threadId:
-            }],
-            added: [{
-                messageId: "fm1u316",
-                threadId: "afed75fb4f512ae7",
-                index: 0
-            }, {
-                messageId: "fm1u315",
-                threadId: "b4aae3925af0a0a2",
-                index: 1
-            }],
-            total: 1214
-        }, "call2" ],
-        [ "messageUpdates", {
-            accountId: 'user@example.com',
-            oldState: "m815034",
-            newState: "m815039",
-            changed: [ "fm1u316", "fm1u315", "fm1u314" ]
-            removed: []
-        }, "call3" ],
-        [ "messages", {
-            accountId: 'user@example.com',
-            state: "m815039",
-            list: [{
-                "id": "fm1u316",
-                "threadId": "afed75fb4f512ae7",
-                "mailboxId": "mailbox1",
-                "isUnread": true,
-                "isFlagged": false,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": false,
-                "from": [
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" }
-                ],
-                "subject": "Camping trip",
-                "date": "2014-07-24T15:04:51Z",
-                "preview": "Hey Joe. Fancy a trip out west next week? I hea…"
-            }, {
-                "id": "fm1u315",
-                "threadId": "b4aae3925af0a0a2",
-                "mailboxId": "mailbox1",
-                "isUnread": true,
-                "isFlagged": false,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": false,
-                "from": [
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" }
-                ],
-                "subject": "Camping trip",
-                "date": "2014-07-24T15:04:51Z",
-                "preview": "Hey Joe. Fancy a trip out west next week? I hea…"
-            }, {
-                "id": "fm1u314",
-                "threadId": "4f512aafed75e7fb",
-                "mailboxId": "mailbox1",
-                "isUnread": false,
-                "isFlagged": false,
-                "isAnswered": false,
-                "isDraft": false,
-                "hasAttachment": false,
-                "from": [
-                    { name: "Joe Bloggs", email: "joebloggs@fastmail.fm" }
-                ],
-                "to": [
-                    { name: "Jane Doe", email: "janedoe@fastmail.fm" }
-                ],
-                "subject": "Camping trip",
-                "date": "2014-07-24T15:04:51Z",
-                "preview": "Hey Joe. Fancy a trip out west next week? I hea…"
-            }],
-            notFound: null
-        }, "call3" ],
-        [ "threadUpdates", {
-            accountId: 'user@example.com',
-            oldState: "mc1264092",
-            newState: "mc1264097",
-            changed: [ "afed75fb4f512ae7", "b4aae3925af0a0a2" ],
-            removed: []
-        }, "call4" ]
-        [ "threads", {
-            accountId: 'user@example.com',
-            state: "mc1264097",
-            list: [{
-                id: "afed75fb4f512ae7",
-                messageIds: [ "fm1u241", "fm1u316" ]
-            }, {
-                id: "b4aae3925af0a0a2",
-                messageIds: [ "fm1u315" ]
-            }],
-            notFound: null
-        }, "call4" ]
-    ]
-
-Here's how we apply this information to our current state to stay in sync:
-
-1. `mailboxUpdates`/`mailboxes`: The inbox is the only mailbox to have changed, and only the counts have changed. The new counts are returned in the `mailboxes` response, so we just need to update our cache with the new properties to bring us fully in sync.
-2. `messageListUpdates`: This is more interesting.
-
-    Suppose the client has a sparse list of messageIds (for example, the user opened the folder, loading the first section, then jumped to the middle):
-
-        [ 'm1u1', 'm1u2', 'm1u3', 'm1u4', -, -, -, 'm1u8', -, ...]
-
-    To update this to match the new server state:
-
-    1. Check the oldState property matches the current state of the list. Also check the sort and search are the same. If any of these don't match, abort and re-request an update from the actual current state.
-    2. If the newState property is the same as the current state of the list, nothing to do, so return.
-    3. If there's an `uptoMessageId`, search for this id. If found, remove anything after it in the list. If not found, abort, reset the list and start again with `getMailboxMessageList`.
-    4. Search for each of the messageIds in the `removed` list and remove them from the client's list (without leaving a gap – this is a splice operation). If any can't be found, keep processing but after finishing, null out anything after the first gap in the list. e.g. referring to the example sparse list above, 'm1u8' would be removed as it's after the first gap.
-    5. Iterate through the `added` list **in order**, inserting the messageIds at the positions indicated (again this is a splice operation and will shift everything else further along).
-    6. Set the list length to that given in the `total` property.
-
-    Note, adding or removing an item to/from the list shifts the position of
-    everything around it.
-
-    e.g. adding 'm2' in position 2: `[ 'm1', 'm3' ] -> [ 'm1', 'm2', 'm3' ]`
-
-    e.g. removing 'm2': `[ 'm1', 'm2', 'm3' ] -> [ 'm1', 'm3' ]`
-
-3. `messageUpdates`: The `removed` array has 0 length (so, although a message was removed from the Inbox message list, it has not been deleted: it is simply no longer the first message in the thread in the given sort order). The `changed` array has three message ids: the first two we don't have in memory, so we can ignore, but the last one we have in our cache so we should mark is as needing an update (i.e. the flags might be out of date).
-4. `messages`: Because we specified a `fetchMessages` argument to `getMessageUpdates`, this response contains the requested data for the messages that have been modified or added. The first two are new messages, and we can add this data to our header cache information so we have the info we need to display the mailbox. The final message is one we already have in cache, but a property of it has now changed (in this case, it is no longer unread). We can update this data and clear the flag we set in step 3 indicating it needs refreshing (the reason we set the flag is so the `messageUpdates` response is correctly handled regardless of whether we specified `fetchMessages` or not.
-5. `threadUpdates`: There are two changed threads here. One existing thread has a new message in it, the other is a brand new thread to create in our cache.
-
-After applying these changes, or cache is completely in sync with the server, even though we only have a partial data set cached, and we only made a single HTTP request.
-
-Now let's look at the more difficult cases, when errors occur.
-
-### Handling errors
-
-The most common error will be a `tooManyChanges` error, when the number of changes exceeds the number we were prepared to accept. In this case, it would be more efficient to throw away much of our cache, or mark it as potentially dirty, and then just fetch the information we need right now (similar to the cold boot situation), rather than fetching a potentially large set of updates. Let's look at the errors for each method.
-
-`getMessageListUpdates`: If there are more changes than the `maxChanges` argument, or the server is unable to calculate updates from the state requested, you will receive an appropriate error back. In these cases, you simply have to throw away the current message list cache and request the section you are interested in with a standard `getMessageList` call.
-
-`getMessageUpdates`: If there are too many changes, you will get an error back. In this case, there are two strategies you could adopt. The first is simple: mark each message you have in cache as needing an update, and fetch these when the message is next required by the user (note, since only the flags and mailboxes it belongs to are mutable, the data you need to fetch can be reduced). As an extra possible optimisation, you could try another `getMessageUpdates` call first, with a higher `maxChanges`, but with `fetchMessages` set to `null`. If it succeeds, this will give you back an exact list of the message ids for messages with changes, so you can mark just those as needing a refresh rather than every message in your cache. If there are still too many changes, you will have to fall back to refetching all the flags and labels.
-
-`getThreadUpdates`: Just like with `getMessageUpdates`, if there are too many changes you can either flush your cache of threads, or try with a higher `maxChanges` but `fetchThreads: false`.
-
-In each of the error cases, a single further HTTP request should be sufficient to get the necessary data to update the client to the correct state.
-
-### Handling message list without delta updates
-
-The response to `getMessageList` includes a property called `canCalculateUpdates`, which lets you know whether the server supports a call to `getMessageListUpdates` for the given message list (servers may not support it at all, or may only support it when there is no search involved etc.). In this case, we have to fetch the bit of the message list currently on display, and just throw away all of our cached data for the message list. Note, however, we don't have to throw away all the message data, we just have to fetch the message ids, so it's still not too inefficient.
-
-## Performing actions
-
-Modifying the state is the most complex operation to get right, as changes to messages may change counts in mailboxes, or thread membership, and the client may not have sufficient information to work out all the effects; it must apply the change then get the rest of the updates from the server.
-
-### Selecting all
-
-If you select all, you need to fetch the complete message list for the mailbox currently displayed.
-
-When applying actions to threads, you will often wish to apply the same action to every message in the thread in the same mailbox as the one explicitly selected, or even to all the messages in the thread, regardless of mailbox. The most efficient way to do the former is to fetch the same message list, but with `collapseConversations == false`. You can then easily build a map of threadId to messages ids for messages in the list. If you need to find all messages in the thread, regardless of message list, you will need to fetch the Thread object for every thread in the message list (but you don't need to fetch any message details). You can do this in a single `getMessageList` call, with `fetchThreads: true`, but `fetchMessages: null`, although if it's a long message list you may wish to break this up into a few calls to avoid a single large request blocking the UI for too long.
-
-### Moving a message
-
-Moving a message from the Inbox to the Trash is simple:
-
-    [ "setMessages", {
-        update: {
-            "fm1u254": {
-                mailboxes: [ "mailbox3" ]
-            }
-        }
-    }, "call1" ]
-
-We can then do a resync as in section 4 to update the client cache. This is easy, but means there's a noticeable delay performing every action, and we cannot operate in an offline mode and resync later. We can improve on this, although it makes it considerably more complicated. We can apply the effects we think the changes will have instantly then compare this with the results we get back from the server.
-
-1. **Mailbox counts**.
-
-   For each message moved:
-   - Decrement the `totalMessages` count for the source mailbox.
-   - Increment the `totalMessages` count for the destination mailbox.
-   - If unread, decrement the `unreadMessages` count for the source mailbox.
-   - If unread, increment the `unreadMessages` count for the destination mailbox.
-
-   If there are no other messages in the thread in the source mailbox:
-   - Decrement the `totalThreads` count for the source mailbox.
-   - If any of the messages in the thread are unread, decrement the
-     `unreadThreads` count for the source mailbox.
-
-   If there are no other messages in the thread already in the destination mailbox:
-   - Increment the `totalThreads` count for the destination mailbox.
-   - If any of the messages in the thread are unread, increment the
-     `unreadThreads` count for the destination mailbox.
-
-   There are slight added complexities if moving a message into or out of the Trash; refer to the spec for full details.
-
-2. **Message list**. Splice each selected message being moved from the current message list (from which it was selected by the user). You may also try to insert them at the correct position in the message list of the destination mailbox:
-
-   From the thread object, you know if there are any other messages in the thread already in the mailbox. As the message list in the thread object is sorted by date, as long as the mailbox list is also sorted by date, you can now work out if the message is a new exemplar in the message list or not. If it is, binary search the list by date to find where to insert it. If you don't have message headers for all the message list loaded, you may not be able to determine the correct spot. In this case, it should be inserted anyway so the user can access it; the mistake will be corrected when the next sync with the server occurs.
-
-3. **Thread**. No changes to make.
-
-4. **Message**. Update the `mailboxIds` property on the message object.
-
-When you next do resync, whether that's a second or several days later, apply the changes and fetch the delta update (as per section 4) in a single request, and when the response is received, undo the preemptive changes made above and apply the real changes. Hopefully the end result will be the same, and there will be no noticeable difference to the user.
-
-## Keeping a full copy of mail in sync
-
-JMAP can also be used to efficiently download and keep in sync the entire set of a user's mail. To do this, you would firstly get the full set of mailboxes, then page in the full list of message ids and download the actual messages. From this point on, you would only need to get the delta updates. Presuming you also want to optimise for fetching new mail first, you could do something like when you want to get the latest updates:
-
-    [
-        [ "getMailboxUpdates", {
-            sinceState: "m123456789",
-            fetchRecords: true,
-            fetchRecordProperties: null
-        }, "call1" ],
-        [ "getMessageUpdates", {
-            sinceState: "m815034"
-            maxChanges: 50,
-            fetchRecords: true,
-            fetchRecordProperties: [
-                "threadId",
-                "mailboxIds",
-                "isUnread",
-                "isFlagged",
-                "isAnswered",
-                "isDraft",
-                "hasAttachment",
-                "labels",
-                "from",
-                "to",
-                "subject",
-                "date",
-                "preview"
-            ]
-        }, "call3" ],
-        [ "getMessageList", {
-            filter: {
-                inMailboxes: [ "${inboxId}" ]
-            },
-            sort: [ "date desc", "id desc" ]
-            collapseThreads: false,
-            position: 0,
-            limit: 100
-        }, "call2" ]
-    ]
-
-The first two calls get the delta updates to mailboxes and messages (if the client has a copy of all messages locally, it has no need to use the server to get threads as it already has all the information). In the common case where there are fewer than 50 message changes since last time, this will bring the client fully up to date (barring downloading new message bodies, attachments and other details, which it can easily schedule at its leisure).
-
-However, if there have been a large number of changes since last time, the client is not yet fully up to date. It can continue to call getMessageUpdates to fetch more changes until it reaches the current state. Simultaneously, it can use the response to the getMessageList call in the first request to see if there are any new message ids at the top of the Inbox. These can be downloaded first so the user has immediate access to new messages (by the end of the second round trip) while the other changes then continue to sync across afterwards.
diff --git a/server/protocols/jmap-draft/doc/specs/home/faq.mdwn b/server/protocols/jmap-draft/doc/specs/home/faq.mdwn
deleted file mode 100644
index a481f13..0000000
--- a/server/protocols/jmap-draft/doc/specs/home/faq.mdwn
+++ /dev/null
@@ -1,49 +0,0 @@
-# JMAP
-
-<p style="margin-left:-40px;"><iframe width="780" height="469" src="http://www.youtube.com/embed/8qCSK-aGSBA?version=3&amp;rel=0&amp;fs=1&amp;showsearch=0&amp;showinfo=1&amp;iv_load_policy=1&amp;wmode=transparent" frameborder="0" allowfullscreen="true"></iframe></p>
-
-## What is JMAP?
-
-JMAP is a transport-agnostic, stateless JSON-based API for synchronising a mail client with a mail server. It is intended as a replacement for IMAP. The specification is based on the API currently used by [FastMail](https://www.fastmail.com)'s web app.  It aims to be compatible with the IMAP data model, so that it can be easily implemented on a server that currently supports IMAP, but also allows for reduced data usage and more efficient synchronisation, bundling of requests for latency mitigation and is generally much easier to work with than IMAP.
-
-## Why is it not REST based?
-
-JMAP is actually more REST-like than most "RESTful" APIs. It is stateless, highly cacheable, supports transparent intermediaries and provides a uniform interface for manipulating different resources. However, it doesn't use HTTP verbs to implement this.
-
-When you have a high latency connection (such as on a mobile phone, or even wired connections from the other side of the world), the extra round trips required for an HTTP REST-based protocol can make a huge impact on  performance. This is especially an issue when you have an order-dependency in your API calls and you need to make sure one has finished before the other can be run (for example when you mutate the state of a message then want to fetch the changes to a mailbox containing the message). In the JMAP protocol, this can be done in a single round trip. An HTTP REST-based version would require two full round trips for the same operation.
-
-The JMAP protocol is transport agnostic and can be easily transported over a WebSocket, for example, as well as HTTP.
-
-## Why is it not a binary protocol?
-
-A binary protocol would be arguably more compact and faster to encode/parse. However, history has shown text-based protocols are much easier to debug, and by using an existing widely-used encoding format (JSON) we make it much easier for developers to use this protocol. No need to write new custom, error-prone parsers. The difference in speed is likely to be minimal, especially if you GZIP the exchange and use WebSockets instead of HTTP.
-
-## Why do labels apply to messages not threads?
-
-Mutable state has to be stored per-message, for example the `isUnread` status must apply on a per message basis, and it's very useful to be able to flag a particular useful message rather than just the whole thread. To be able to delete a particular message to the Trash out of a thread, you need to be able to change the mailbox of that message. Sent messages should belong to the sent mailbox, but not messages you receive.
-
-Meanwhile, it is simple to aggregate the information of the messages in the thread. So, for example, if any message in the thread is unread, then the thread can be considered unread. There is no need to store mutable state as a property of a thread therefore, and the less mutable state, the easier it is to manage. Finally, all known existing IMAP implementations, plus Gmail, store this state per-message, not per-thread, so it makes it easier for implementors to migrate to JMAP.
-
-## Why are there flags (e.g. isUnread) separate to mailboxes?
-
-In IMAP, you can only have one mailbox but you can have multiple flags on a single message. In other systems (where you have labels), these are really the same thing and you can have a single message in multiple mailboxes. JMAP aims to support both, so it has to be able to specify whether a mailbox can be used in combination with other mailboxes on a message, or must be the only one with the message (but does allow different flags). The clearest way of specifying what is allowed by the server is to keep the mailboxes separate to flags in JMAP as well.
-
-## Why isUnread instead of isRead?
-
-Although this may seem inconsistent at first, it actually makes more sense. The "special" status is when a message is unread (this is what clients are interested in), so like isDraft, isFlagged and isAnswered, we make the special status equal to `true`. It is also consistent with the need for unread counts in mailbox objects, not read counts, and makes the definition of sorting a message list the same for isFlagged and isUnread when conversations are collapsed.
-
-## I want to get involved with JMAP. What do I need to know?
-
-First of all, you should join the [JMAP mailing list](https://groups.google.com/forum/#!forum/jmap-discuss). Feedback is welcome: send your thoughts or comments on anything that is imprecise, incomplete, or could simply be done better in another way. Or if you're working on something JMAP related, this list is a good place to let other people know and to raise any issues you come across.
-
-The specification itself is [hosted on GitHub](https://github.com/jmapio/jmap). If you've found a typo or other minor change, feel free to just submit a pull request. Otherwise, discussion on the mailing list first is preferred.
-
-## I want to implement it. What do I need to know?
-
-That's great! There are lots of resources on this website to help you. Counter-intuitive though it may seem, I recommend starting with the [guide for client authors](client.html) to get a good feel for how the JMAP spec works. After that though, [the spec](spec.html) is your bible and the [advice for implementors](server.html) is your friend.
-
-If you're implementing the spec and suddenly find there's an externally visible behaviour that's not specified, please email the [mailing list](https://groups.google.com/forum/#!forum/jmap-discuss) so we can update the spec to nail down this corner.
-
-## I want to use it to build a client. What do I need to know?
-
-Have a read through the [client guide](client.html) to get an idea of how it works. Then you'll want to find a JMAP server to test against.
diff --git a/server/protocols/jmap-draft/doc/specs/server-guide/jmap-server-guide.mdwn b/server/protocols/jmap-draft/doc/specs/server-guide/jmap-server-guide.mdwn
deleted file mode 100644
index 7e8b056..0000000
--- a/server/protocols/jmap-draft/doc/specs/server-guide/jmap-server-guide.mdwn
+++ /dev/null
@@ -1,312 +0,0 @@
-# Advice for JMAP implementors
-
-This document describes a recommended set of data structures and algorithms for efficiently implementing JMAP. It is intended to serve as suggestions only; there may well be better ways to do it. The spec is the authoritative guide on what constitutes a conformant JMAP implementation.
-
-## Assigning Message Ids
- 
-A good way of assigning a message id is to sha1 the RFC2822 message and take the first 80 bits (this is sufficient for avoiding collisions in any reasonable sized instance, but the server may choose any length it likes). If this is already in use (i.e. another copy of the message already exists), sha1 the GUID. Repeat until there is no collision. The bytes can be represented in any reasonable encoding for transmission as a `String` within the API.
-
-## Modification sequences
-
-A modification sequence, or **modseq**, is a 63-bit monotonically incrementing counter. Each user has their own modseq counter (in IMAP it's originally per-mailbox, but per user is backwards compatible with this). Every time a change occurs to data within the user, the modseq is incremented by one and the new value is associated with the changes. This is used in a number of data structures and algorithms below to efficiently calculate changes.
-
-## Data structures
-
-As ever in programming, get your data structures right and the server will practically write itself.
-
-There are three types of data structures suggested in this guide (excluding the email indexing for search, which is more complicated than this guide is prepared to go into):
-
-1. A **Map** (associative array) of id to variable-length data blob:
-   - Insert should be O(1).
-   - Lookup should be O(1).
-   - Delete should be O(1).
-   - Not ordered, but we need to be able to iterate over all the values.
-2. An **append-only log** of variable length blobs:
-   - Insert is only at tail.
-   - Lookup needs to be able to binary search (based on modseq) to find start location, then read sequentially.
-   - Delete is only at head.
-3. An **Ordered list** of fixed-sized objects:
-   - Insert is mainly at or near the head, but can be in a random location.
-   - Want to be able to read sequentially from head (common case), or read whole structure into memory and sort.
-   - Delete is mainly near head, but can be in a random location.
-
-### 1. User
-
-This is a simple collection of important top-level values for the user (kept in a **Map**):
-
-- Highest ModSeq for the user (`63 bits` for IMAP compatibility)
-- Highest ModSeq of any Mailbox.
-- Low watermark ModSeq for Thread Log `63 bits` – whenever the thread log (see below) is truncated, the modseq of the new first item in the log is stored here. Any attempt to call *getThreadUpdates* with a modseq lower than this must be rejected with a `cannotCalculateUpdates` error.
-- Low watermark ModSeq for Message Log `63 bits` – the same, but for the message log.
-- Quota available in bytes (63 bits. -1 => Don't care)
-- Quota used in bytes `63 bits`
-- List of other users with mailboxes made available to this user (shared mailboxes) (var length).
- 
-### 2. Mailboxes
-
-A map of Mailbox Id to all of the mailbox object values (as defined in the spec, including the counts). For the purposes of calculating `getMailboxUpdates`, also include the following for each mailbox:
-
-- **Mailbox ModSeq** `63 bits` Updated when any of the mailbox properties (as defined in the spec) change – not updated when the mailbox message list changes.
-- **Mailbox Non-Counts ModSeq** `63 bits` Updated when any of the mailbox properties that are not counts change. When calculating `getMailboxUpdates`, if this is lower than the previous mod seq, but the Mailbox ModSeq is higher, then only counts have changed.
-- **Message list UID Next** `32 bits` The next UID (incrementing 32-bit counter) to assign to messages added to the mailbox message list.
-- **Message list highest ModSeq** `63 bits` The highest ModSeq of a message in the mailbox message list.
-- **Low watermark message list ModSeq** `63 bits` Whenever a message is undeleted via IMAP (that is the \Deleted flag is removed) or a deleted message is fully expunged from the mailbox message list (see message list data structure below), set this to the ModSeq of that message if higher than the previous value. Any attempt to call *getMessageListUpdates* with a modseq lower than this must be rejected with a `cannotCalculateUpdates` error.
-
-The suggested way to assign a Mailbox id is to use an incrementing counter. 16 bits should be sufficient, giving support for a maximum of 65536 mailboxes per user. Old ids are reusable without penalty while keeping with the JMAP semantics. Over the wire this would be represented as a prefix plus an encoding of the number, e.g. "m1", "m2" etc.
-
-### 3. Mailbox message list
-
-One of these should exist for each mailbox. It is an ordered list of fixed size objects. Each object is:
-
-- **UID** `32 bits` As per IMAP semantics, this increments each time you append a message to the mailbox. The next UID to use is stored in the Mailbox object.
-- **ModSeq** `63 bits` As per IMAP semantics, this is updated whenever any property of the message changes.
-- **Message Id** `80 bits` This is the ID always used within JMAP to refer to the message, and does not change as it moves between mailboxes.
-- **Thread Id** `64 bits`
-- **Deleted** `64 bits` Time stamp from epoch of when the message was removed the mailbox. `0` means not deleted. See below
-- **Message Date** `64 bits`
- 
-Given that most message list fetches are of the first section of a single mailbox in date descending order, if we keep it stored in this order on disk we can just read the first few bytes to return the desired information; no sorting required (we don't even have to look up anything in the message cache). The date is included in each record so we can insert a new one in the correct position without having to reference any other data.
-
-When a message is removed from the mailbox we don't remove it immediately from the message list. Instead we just set the Deleted field to the time stamp of when it was deleted. This allows the delta update algorithm to work to efficiently update the client to the new list state. At certain intervals (either based on how many deleted nodes there are or how long since they were deleted) this needs to be cleaned up and the deleted objects fully expunged from the message list. At this point the low watermark ModSeq needs to be updated on the Mailbox data structure.
- 
-### 4. Message cache
-
-A map of Message Id to flags, mailboxes and common header information for that message (variable length). This lets the server optimise calls to `getMessages` that only fetch this information (common when fetching the required information for a mailbox listing). It also allows optimisation of `getMessageList` as most, even quite complex filters will only require checking this cache (which should be relatively fast) rather than looking up and parsing the whole message (which could be quite slow).
-
-For each message, store:
-
-- **isUnread** `1 bit` (mutable)
-- **isFlagged** `1 bit` (mutable)
-- **isAnswered** `1 bit` (mutable)
-- **isDraft** `1 bit` (mutable by IMAP)
-- Other flags + labels + annotations as needed for IMAP compatibility only. (mutable)
-- **Mailboxes** (list of mailbox ids this message belongs to) (mutable)
-- **From** header (either in the JSON used by JMAP, or whatever IMAP needs)
-- **To** header (either in the JSON used by JMAP, or whatever IMAP needs)
-- **Subject** header
-- **Date**
-- **Preview** (see spec)
-- **Attachments** (list of file names only; also used for hasAttachments)
-- **GUID** (sha1 of raw email)
- 
-### 5. Message index
-
-For searching messages at any reasonable speed, an index is required from textual content within the message to the message id. Describing how this should work is beyond the scope of this guide.
-
-### 6. Messages
-
-A map of GUID (sha1 of the message contents) to the raw RFC2822 message itself. This is the bulk of the data to be stored for each user. As this data is immutable and referenced by its sha1, there are many possibilities for how to store it. Each message could be stored as a separate file using its GUID as the name. Or on a separate networked object store etc. etc.
- 
-### 7. Message log
-
-An append only log of changes made to the message cache. Each entry consists of:
-
-- **ModSeq** of change `63 bits`
-- List of **Message ids** which were affected by the change (to save extra lookups when the client is fetching these changes, should probably include a bit to say whether the message was destroyed by the change or created/modified).
- 
-Periodically, truncate the log by chopping records off the beginning. At this point you need to update the low watermark ModSeq for the log in the User object (data structure 1).
- 
-### 8. Threads
-
-A map of thread id to an object containing information about the thread (variable length). The information stored for each thread is:
-
-- The **list of messages** belonging to the thread, sorted in date order. For each - message we need to store:
-  - **Message Id** `80 bits`
-  - **Mailboxes** The list of mailboxes the message belongs to. For sorting a message list by thread unread/thread flagged purposes only.
-  - **isUnread** `1 bit` For sorting a message list by thread unread/thread flagged purposes only.
-  - **isFlagged** `1 bit` For sorting a message list by thread unread/thread flagged purposes only.
-- **ModSeq of last change** to isRead/isFlagged for any message in the thread (for sorting a message list by thread unread/thread flagged purposes only).
-- **Sha1 of subject** after stripping Fwd/Re etc. (for checking whether we need to split a new conversation out later; see threading algorithm in data structure 10).
- 
-### 9. Thread log
-
-An append only log of changes made to the **membership** of threads (in data structure 7; changes to only the isRead/isFlagged/Mailboxes of a message in a thread do not need to go in the log). Each entry consists of:
-
-- **ModSeq** of change
-- **List of thread ids** which were affected by the change (to save extra lookups when the client is fetching these changes, should probably include a bit to say whether the thread was destroyed by the change or created/modified).
- 
-As with the message log, periodically truncate the log by chopping records off the beginning; at this point you need to update the low watermark ModSeq for the log in the user object.
- 
-### 10. Refs to ThreadId
-
-This is solely used to look up the conversation to assign to a message on creation/import/delivery. Maps the RFC2822 Message Id (eughh, so many different types of id!) to the thread id.
-
-The suggested rule for connecting messages into threads is this:
-
-If two messages share a common RFC2822 Message Id in the set of such ids within the `Message-Id`, `References` and `In-Reply-To` headers of each message, and the messages have the same `Subject` header (after stripping any preceding Re:/Fwd: and trimming white space from either end), then they should belong in the same thread. Otherwise they should belong in different threads.
-
-## Message List Algorithms
-
-The `state` property to return with getter calls to each type is:
-
-- Mailbox: Highest ModSeq of any Mailbox (stored in the User object)
-- MessageList: Message list UID Next + Message list highest ModSeq (as stored on the Mailbox object). For message lists that aren't just a mailbox, the state string should be the highest message ModSeq (as found at the tail of the Message Log).
-- Thread: The highest modseq found at the end of the Thread Log.
-- Message: The highest modseq found at the end of the Message Log.
-
-### getMailboxUpdates
-
-Simply iterate through the set of Mailboxes comparing their current mod seqs to the client mod seq. If higher, the mailbox has changed. If the non-counts is not higher, only counts have changed.
-
-### getMessageList
-
-First you need to get the complete list. In the common case of…
-
-    filter == { inMailboxes: [ 'inboxId' ], notInMailboxes: [ 'trashId' ] }
-
-…you are simply fetching the list of messages in a mailbox. This list is already pre-calculated for each mailbox and kept on disk (data structure 3). You can simply slurp this into memory and sort if needed (again, in the common case of sorting date-descending, it will be pre-sorted).
-
-If the filter is more complex, you will need to do more work to get the set of matching messages and sort it. If there is a `String` component to the filter, first use the message index (data structure 5) to get a set of matches. Then iterate through and lookup each match in the message cache (data structure 4) to apply any other components of the filter. Finally sort as specified.
-
-Once you have the complete message list, you can calculate the results to return to the client. Since a client is likely to fetch a different portion of the same message list soon after, it is beneficial if the server can keep the last list requested by the user in a cache for a short time.
-
-    let collapseThreads = args.collapseThreads
-    let position = args.position
-    let anchor = args.anchor
-    let anchorOffset = args.anchorOffset
-    let limit = args.limit
-    let total = 0
-    let messageIds = [] # NB Max size of array is limit
-    let threadIds = []  # NB Max size of array is limit
-
-    # If not collapsing threads, we can just jump to the required section
-    if !collapseThreads {
-      total = messageList.length
-      for i = position; i < total; i = i + 1 {
-        messageIds.push( msg.id )
-        threadIds.push( msg.threadId )
-      }
-    } else {
-      # Optimisation for the common case
-      let totalIsKnown = filter is just mailbox
-      let SeenThread = new Set()
-      let numFound = 0
-      foreach msg in sortedFilteredList {
-        if !SeenThread{ msg.threadId } {
-          SeenThread.add( msg.threadId )
-          total += 1
-          if position >= total && numFound < limit {
-            messageIds.push( msg.id )
-            threadIds.push( msg.threadId )
-            numFound += 1
-            if numFound == limit && totalIsKnown {
-              break;
-            }
-          }
-        }
-      }
-      if totalIsKnown {
-        total = getTotal( mailbox, collapseThreads )
-      }
-    }
-
-### getMessageListUpdates
-
-With the suggested data structures, we can do delta updates for any standard mailbox message list (the common case), but not for a search. The following algorithm correctly calculates the delta update. We're presuming we already have a `messageList` (directly read in from data structure 3 and sorted if required):
-
-    let index = -1
-    let added = []
-    let removed = []
-    let collapseThreads = args.collapseThreads
-    let uptoHasBeenFound = false
-
-    # A mutable sort is one which sorts by a mutable property, e.g.
-    # sort flagged messages/threads first.
-    let isMutable = sort.isMutable()
-
-    # An exemplar is the first message in each thread in the list, given the
-    # sort order that
-    # The old exemplar is the exemplar in the client's old state.
-    let SeenExemplar = collapseThreads ? new Set() : null
-    let SeenOldExemplar = collapseThreads ? new Set() : null
-
-    foreach msg in messageList {
-
-      let isNewExemplar = false
-      let isOldExemplar = false
-
-      let isNew = ( msg.uid >= mailbox.uidNext )
-      let isChanged = ( msg.modSeq > args.state )
-      let isDeleted = ( msg.deletedTimeStamp != 0 )
-      let wasDeleted = ( isDeleted && !isChanged )
-
-      # Is this message the current exemplar?
-      if !isDeleted && ( !collapseThreads || !SeenExemplar{ msg.threadId } ) {
-        isNewExemplar = true
-        index += 1
-        if collapseThreads {
-          SeenExemplar.set( msg.threadId )
-        }
-      }
-
-      # Was this message an old exemplar?
-      # 1. Must not have arrived after the client's state
-      # 2. Must have been deleted before the client's state
-      # 3. Must not have already found the old exemplar
-      if !isNew && !wasDeleted &&
-          ( !collapseThreads || !SeenOldExemplar{ msg.threadId } ) {
-        isOldExemplar = true
-        if collapseThreads {
-          SeenOldExemplar.set( msg.threadId )
-        }
-      }
-
-      if isOldExemplar && !isNewExemplar {
-        removed.push({
-          messageId: msg.messageId,
-          threadId: msg.threadId
-        })
-      }
-      else if !isOldExemplar && isNewExemplar {
-        added.push({
-          index: index,
-          messageId: msg.messageId,
-          threadId: msg.threadId
-        })
-      }
-      # Special case for mutable sorts (based on isFlagged/isUnread). If
-      # collapseThread == true, the sort is based on the *conversation*
-      # isFlagged/isUnread status.
-      if isMutable && isOldExemplar && isNewExemplar {
-        # Has the isUnread/isFlagged status of the message/thread
-        # (as appropriate) possibly changed since the client's state?
-        let modSeq = collapseThreads ?
-              msg.modSeq :
-              getThread( msg.threadId ).modSeq
-        # If so, we need to remove the exemplar from the client view and add 
-        # it back in at the correct position.
-        if modSeq > args.modSeq {
-          removed.push({
-            messageId: msg.messageId,
-            threadId: msg.threadId
-          })
-          added.push({
-            index: index,
-            messageId: msg.messageId,
-            threadId: msg.threadId
-          })
-        }
-      }
-      # If this is the last message the client cares about, we can stop here
-      # and just return what we've calculated so far. We already know the total
-      # count for this message list as we keep it pre calculated and cached in
-      # the Mailbox object.
-      #
-      # However, if the sort is mutable we can't break early, as messages may 
-      # have moved from the region we care about to lower down the list.
-      if !isMutable && msg.uid == args.upto {
-        break
-      }
-    } # End loop
-
-    total = getTotal( mailbox, collapseThreads )
-
-To implement this algorithm, the server must keep the metadata at least of deleted (expunged) messages for a little while (say 1-2 weeks). This is also very useful for restoring accidentally deleted messages as well. At cleanup time (when actually removing the messages), the server must keep track of the highest modseq of a message it has removed in that mailbox (the modseq would have been set at the original delete time); any requests for updates from a state before this must be rejected, as the results may be incorrect.
-
-## getThreadUpdates
-
-If the client id is lower than the low watermark ModSeq for the Thread Log (as found in the User object), reject with a `cannotCalculateUpdates` error. Otherwise, binary search the Thread log looking for the client modseq, then read sequentially from there to get the changes.
-
-## getMessageUpdates
-
-If the client id is lower than the low watermark ModSeq for the Message Log (as found in the User object), reject with a `cannotCalculateUpdates` error. Otherwise, binary search the Message log looking for the client modseq, then read sequentially from there to get the changes.
diff --git a/server/protocols/jmap-draft/doc/specs/software/software.mdwn b/server/protocols/jmap-draft/doc/specs/software/software.mdwn
deleted file mode 100644
index 5570c41..0000000
--- a/server/protocols/jmap-draft/doc/specs/software/software.mdwn
+++ /dev/null
@@ -1,19 +0,0 @@
-# JMAP software
-
-JMAP is still young but several implementations already exist, and more are on the way. If you're working on a project that uses JMAP, please email the [mailing list](https://groups.google.com/forum/#!forum/jmap-discuss) and we'll add it here.
-
-## Clients
-
-* **[JMAP Demo Webmail](https://github.com/jmapio/jmap-demo-webmail)** (JavaScript, MIT): a sophisticated demo webmail client to be used as a base for new projects. Default client for the [proxy](https://proxy.jmap.io).
-
-## Servers
-
-* **[JMAP Proxy](https://github.com/jmapio/jmap-perl)** (Perl, MIT): a complete JMAP server implementation that uses any IMAP, CalDAV and CardDAV store as a backend. Also available as a hosted service at [proxy.jmap.io](https://proxy.jmap.io).
-* **[Cyrus IMAP](https://docs.cyrus.foundation/imap/release-notes/3.0/x/3.0.0-beta1.html)** (C, BSD-style): A scalable enterprise-grade IMAP, CalDAV and CardDAV server. The 3.0 series is adding JMAP support.
-* **[salada](https://github.com/robn/salada)** (Rust, MIT): A small standalone JMAP server, suitable for development and experimentation.
-
-## Libraries
-
-* **[JMAP-JS](https://github.com/jmapio/jmap-js)** (JavaScript, MIT): a full implementation of the JMAP mail, calendar and contacts model in JavaScript. It supports asynchronous local changes and is tolerant of network failure – actions will update the UI instantly, then synchronise changes back to the server when it can. It also has multi-level undo/redo support. Used by the [demo webmail](https://github.com/jmapio/jmap-demo-webmail).
-* **[jmap-client](https://github.com/linagora/jmap-client)** (JavaScript ES6, MIT): a simple promise-based API to send requests to a JMAP server.
-* **[jmap-rs](https://github.com/robn/jmap-rs)** (Rust, MIT): JMAP parser, generator and data model library for Rust. Used by [salada](https://github.com/robn/salada).
diff --git a/server/protocols/jmap-draft/doc/specs/spec/account.mdwn b/server/protocols/jmap-draft/doc/specs/spec/account.mdwn
deleted file mode 100644
index c846d27..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/account.mdwn
+++ /dev/null
@@ -1,119 +0,0 @@
-## Accounts
-<aside class="warning">
-Not implemented
-</aside>
-
-A single login may provide access to multiple accounts, for example if another user is sharing their mail with the logged in user.
-
-All data belongs to an account. With the exception of a few explicit operations to copy data between accounts, all methods take an *accountId* argument that specifies on which account the operations are to take place. This argument is always optional; if not specified, the primary account is used. All ids (other than Account ids of course) are only unique within their account.
-
-An **Account** object has the following properties:
-
-- **id**: `String`
-  The id of the account. This property is immutable.
-- **name**: `String`
-  A user-friendly string to show when presenting content from this account. e.g. the email address of the account.
-- **isPrimary**: `Boolean`
-  This MUST be true for exactly one of the accounts returned.
-- **capabilities**: `AccountCapabilities`
-  An object describing general capabilities of this server.
-- **mail**: `MailCapabilities|null`
-  If `null`, this account does not support mail, and any use of the mail-related methods with this account will result in an `accountNoMail` error. Otherwise, it will be an object describing certain capabilities of the mail system.
-- **contacts**: `ContactsCapabilities|null`
-  If `null`, this account does not support contacts, and any use of the contacts-related methods with this account will result in an `accountNoContacts` error. Otherwise, it will be an object describing certain capabilities of the contacts system.
-- **calendars**: `CalendarsCapabilities|null`
-  If `null`, this account does not support calendars, and any use of the calendar-related methods with this account will result in an `accountNoCalendars` error. Otherwise, it will be an object describing certain capabilities of the calendars system.
-
-An **AccountCapabilities** object has the following properties:
-
-- **maxSizeUpload**: `Number`
-  The maximum file size, in bytes, that the server will accept for a single file upload (for any purpose).
-
-A **MailCapabilities** object has the following properties:
-
-- **isReadOnly**: `Boolean`
-  True if the user has read-only access to the mail in this account. The user may not use the `set`-type mail methods with this account.
-- **maxSizeMessageAttachments**: `Number`
-  The maximum total size of attachments, in bytes, allowed for messages. A server MAY still reject messages with a lower attachment size total (for example, if the body includes several megabytes of text, causing the size of the encoded MIME structure to be over some server-defined limit).
-- **canDelaySend**: `Boolean`
-  Does the server support inserting a message into the outbox to be sent later at a user-specified time?
-- **messageListSortOptions**: `String[]`
-  A list of all the message properties the server supports for sorting by. This MAY include properties the client does not recognise (for example custom properties specified in the vendor extension). Clients MUST just ignore any unknown properties in the list.
-
-A **ContactsCapabilities** object has the following properties:
-
-- **isReadOnly**: `Boolean`
-  True if the user has read-only access to the contacts in this account. The user may not use the `set`-type contacts methods with this account.
-
-A **CalendarsCapabilities** object has the following properties:
-
-- **isReadOnly**: `Boolean`
-  True if the user has read-only access to the calendars in this account. The user may not use the `set`-type calendar methods with this account.
-
-The AccountCapabilities, MailCapabilities, ContactsCapabilities and CalendarsCapabilities objects MAY also contain one or more properties prefixed with "vnd-" + a name that uniquely identifies the vendor. For example `"vnd-com.fastmail"`. The value type for these properties is undefined. These properties allow vendors to be able to inform their own clients of custom capabilities. Unknown properties of this form MUST be ignored by clients.
-
-### getAccounts
-
-To fetch the complete list of accounts to which the user has access, make a call to `getAccounts`. It takes a sole, optional argument:
-
-- **sinceState**: `String|null`
-  This is the `state` string from a previous call to *getAccounts*.
-
-The response to *getAccounts* is called *accounts*. It has the following arguments:
-
-- **state**: `String`
-   A string representing the state on the server for **all** the data contained within the Account objects. If the data changes, this string will change.
-- **list**: `Account[]|null`
-  An array of all Account objects. If *sinceState* was supplied and it is identical to the current state, this property is `null`.
-
-The following errors may be returned instead of the `accounts` response:
-
-`invalidArguments`: Returned if the `sinceState` argument is included and has the wrong type.
-
-Example of a successful request:
-
-    ["getAccounts", {}, "#0"]
-
-and response:
-
-    [ "accounts", {
-      "state": "f6a7e214",
-      "list": [
-        {
-          "id": "6asf5",
-          "name": "user@example.com",
-          "isPrimary": true,
-          "capabilities": {
-            maxSizeUpload: 1000000000
-          },
-          "mail": {
-            "isReadOnly": false,
-            "maxSizeMessageAttachments": 50000000,
-            "canDelaySend": true,
-            "messageListSortOptions": [
-              "id", "date", "subject", "from", "to",
-              "isUnread", "isPinned", "threadUnread", "threadPinned"
-            ],
-          },
-          "contacts": {
-            "isReadOnly": false
-          },
-          "calendars": {
-            "isReadOnly": false
-          }
-        },
-        {
-          "id": "e12e1a",
-          "name": "shared.user@example.com",
-          "isPrimary": false,
-          "capabilities": {
-            maxSizeUpload: 0
-          },
-          "mail": null,
-          "contacts": null,
-          "calendars": {
-            "isReadOnly": true
-          }
-        }
-      ]
-    }, "#0" ]
diff --git a/server/protocols/jmap-draft/doc/specs/spec/apimodel.mdwn b/server/protocols/jmap-draft/doc/specs/spec/apimodel.mdwn
deleted file mode 100644
index 2498640..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/apimodel.mdwn
+++ /dev/null
@@ -1,259 +0,0 @@
-# JMAP: The Spec
-
-<div id="last-update">Last updated 2016-01-15</div>
-
-This is a specification. It is meant to be readable but it also has to be comprehensive, so it can be dense in places. If you want to get a quick idea of how JMAP works, you should probably read the [guide for client developers](client.html) first. This has lots of example exchanges and should give you a good feel for what JMAP is all about. The spec is heavier going; it attempts to document exactly what each method should do, and what should happen in a myriad of edge cases.
-
-There are undoubtably edge cases that are not yet covered. If you find one, please email <editor@jmap.io> or make a pull request on GitHub if you have a proposed fix.
-
-The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119).
-
-## The JSON API model
-
-JSON is a text-based data interchange format as specified in [RFC7159](https://tools.ietf.org/html/rfc7159). The I-JSON format defined in [RFC7493](https://tools.ietf.org/html/rfc7493) is a strict subset of this, adding restrictions to avoid potentially confusing scenarios (for example, it mandates that an object MUST NOT have two properties with the same key). All data sent from the client to the server or from the server to the client MUST be valid I-JSON according to the RFC, encoded in UTF-8.
-
-### The structure of an exchange
-
-The client initiates an API request by sending the server a JSON array. Each element in this array is another array representing a method invocation on the server. The server will process the method calls and return a response consisting of an array in the same format. Each method call always contains three elements:
-
-1. The **name** of the method to call, or the name of the response from the server. This is a `String`.
-2. An `Object` containing *named* **arguments** for that method or response.
-3. A **client id**: an arbitrary `String` to be echoed back with the responses emitted by that method call (as we'll see lower down, a method may return 1 or more responses, as some methods make implicit calls to other ones).
-
-Example query:
-
-    [
-      ["method1", {"arg1": "arg1data", "arg2": "arg2data"}, "#1"],
-      ["method2", {"arg1": "arg1data"}, "#2"],
-      ["method3", {}, "#3"]
-    ]
-
-The method calls MUST be processed sequentially, in order. Each API request
-(which, as shown, may contain multiple method calls) receives a JSON
-response in exactly the same format. The output of the methods MUST be added
-to the array in the same order as the methods are processed.
-
-Example response:
-
-    [
-      ["responseFromMethod1", {"arg1": 3, "arg2": "foo"}, "#1"],
-      ["responseFromMethod2", {"isBlah": true}, "#2"],
-      ["anotherResponseFromMethod2", {"data": 10, "yetmoredata": "Hello"}, "#2"],
-      ["aResponseFromMethod3", {}, "#3"]
-    ]
-
-### Errors
-
-If the data sent as an API request is not valid JSON or does not match the structure above, an error will be returned at the transport level. For example, when using JMAP over HTTP, a `400 Bad Request` error will be returned at the HTTP level.
-
-Possible errors for each method are specified in the method descriptions. If a method encounters an error, an `error` response must be inserted at the current point in the output array and, unless otherwise specified, no further processing must happen within that method.
-
-Any further method calls in the request MUST then be processed as normal.
-
-An `error` response looks like this:
-
-    ["error", {
-      type: "unknownMethod"
-    }, "client-id"]
-
-The response name is `error`, and it has a type property as specified in the method description. Other properties may be present with further information; these are detailed in the method descriptions where appropriate.
-
-If an unknown method is called, an `unknownMethod` error (this is the type shown in the example above) MUST be inserted and then the next method call MUST be processed as normal.
-
-If an unknown argument or invalid arguments (wrong type, or in violation of other specified constraints) are supplied to a method, an `invalidArguments` error MUST be inserted and then the next method call MUST be processed as normal.
-
-### Vendor-specific extensions
-
-Individual services will have custom features they wish to expose over JMAP. This may take the form of extra datatypes and/or methods not in the spec, or extra arguments to JMAP methods, or extra properties on existing data types (which may also appear in arguments to methods that take property names). To ensure compatibility with clients that don't know about a specific custom extension, and for compatibility with future versions of JMAP, the server MUST ONLY expose these extensions if the client explicitly opts in. Without opt-in, the server MUST just follow the spec and reject anything that does not conform to it as specified.
-
-Any vendor extensions supported by the server are advertised to the client in the capabilities property on the Account object. The client opt-in happens at the transport layer (see the next section).
-
-### JMAP over HTTPS
-
-To make an API request over HTTP (support for other protocols may be added in future extensions to the spec), the client makes an authenticated POST request to the API URL; see the Authentication section of the spec for how to discover this URL and how to authenticate requests.
-
-The request MUST have a content type of `application/json` and be encoded in utf-8.
-
-The request MAY include an `X-JMAP-Version` header, the value of which is a number for the spec version the client would like to use (the list of versions supported by each account can be discovered by the client by inspecting the capabilities property of the Account object). If omitted, the server MUST presume a value equal to the *lowest* supported version common to all accounts.
-
-The request MAY include an 'X-JMAP-Extensions' header, the value of which is a comma-separated list of 'vendor-extension-name:extension-version` the client would like to use. For example, "com.fastmail.message:1,com.fastmail.savedSearch:4". Any white-space should be ignored when evaluating this header.
-
-The server will respond with one of the following HTTP response codes:
-
-#### `200`: OK
-
-The API request was successful. The response will be of type `application/json` and consists of the response to the API calls, as described above.
-
-#### `400`: Bad Request
-
-The request was malformed. For example, it may have had the wrong content type, or have had a JSON object that did not conform to the API calling structure (see *The structure of an exchange* above). The client SHOULD NOT retry the same request.
-
-#### `401`: Unauthorized
-
-The `Authorization` header was missing or did not contain a valid token. Reauthenticate and then retry the request. There is no content in the response.
-
-#### `404`: Not Found
-
-The API endpoint has moved. See the Authentication section of the spec for how to rediscover the current URL to use. There is no content in the response.
-
-#### `412`: Precondition Failed
-
-This means either the JMAP version specified in an `X-JMAP-Version` header, or an extension/version specified in an `X-JMAP-Extensions` header is not supported by one of the accounts used in a method.
-
-After authentication, but before processing any methods, the server MUST scan the methods in the request and check the requested JMAP version and extensions are supported by all the accounts referenced by the methods (unknown account ids MUST just be skipped; these method calls will error out when processed). If there are any accounts that do not support the requested version and extensions, this HTTP error code is returned and the server MUST NOT process any of the methods.
-
-#### `500`: Internal Server Error
-
-Something has gone wrong internally, and the server is in a broken state. Don't automatically retry. There is no content in the response.
-
-#### `503`: Service Unavailable
-
-The server is currently down. Try again later with exponential backoff. There is no content in the response.
-
-### Security
-
-As always, the server must be strict about data received from the client. Arguments need to be checked for validity; a malicious user could attempt to find an exploit through the API. In case of invalid arguments (unknown/insufficient/wrong type for data etc.) the method should return an `invalidArguments` error and terminate.
-
-### Concurrency
-
-To ensure the client always sees a consistent view of the data, the state accessed by a method call MUST NOT change during the execution of the method, except due to actions by the method call itself. The state MAY change in-between method calls (even within a single API request).
-
-### The Number datatype
-
-The JSON datatypes are limited to those found in JavaScript. A `Number` in JavaScript is represented as a signed double (64-bit floating point). However, except where explicitly specified, all numbers used in this API are unsigned integers <= 2^53 (the maximum integer that may be reliably stored in a double). This implicitly limits the maximum length of message lists in queries and the like.
-
-### The Date datatypes
-
-Where the API specifies `Date` as a type, it means a string in [RFC3339](https://tools.ietf.org/html/rfc3339) *date-time* format, with the *time-offset* component always `Z` (i.e. the date-time MUST be in UTC time) and *time-secfrac* always omitted. The "T" and "Z" MUST always be upper-case. For example, `"2014-10-30T14:12:00Z"`.
-
-Where the API specifies `LocalDate` as a type, it means a string in the same format as `Date`, but with the `Z` omitted from the end. This only occurs in relation to calendar events. The interpretation in absolute time depends upon the time zone for the event, which MAY not be a fixed offset (for example when daylight saving time occurs).
-
-### Use of `null`
-
-Unless otherwise specified, a missing property in a request, response or object MUST be intepreted exactly the same as that property having the value `null`. If `null` is not a valid value for that property this would typically cause an error to occur. This rule does not apply to the [top-level datatypes](#data-model-overview), where a missing property usually indicates that the sender wants to leave the existing property value untouched (e.g. in a [*setFoos*](#setfoos) request or a [*getFooUpdates*](#getfooupdates) response).
-
-### CRUD methods
-
-JMAP defines various types of objects and provides a uniform interface for creating, retrieving, updating and deleting them. A **data type** is a collection of named, typed properties, just like the schema for a database table. Each row of the table is a **record**. For a `Foo` data type, records of that type would be fetched via a `getFoos` call and modified via a `setFoos` call. Delta updates may be fetched via a `getFooUpdates` call. These methods all follow a standard format as described below.
-
-### getFoos
-
-Objects of type **Foo** are fetched via a call to *getFoos*. Methods with a name starting with `get` MUST NOT alter state on the server.
-
-This method may take some or all of the following arguments. The getter for a particular data type may not implement all of the arguments (for example if the data type only has two properties, there is little point in being able to just return one of them etc.); see the docs of the type in question. However, if one of the following arguments is available, it will behave exactly as specified below.
-
-- **ids**: `String[]|null`
-  The ids of the Foo objects to return. If `null` then **all** records of the data type are returned.
-- **properties**: `String[]|null`
-  If supplied, only the properties listed in the array are returned for each Foo object. If `null`, all properties of the object are returned. The id of the object is **always** returned, even if not explicitly requested.
-- **sinceState**: `String|null`
-  The *state* argument from a *foos* response may be passed back to future *getFoos* calls as the *sinceState* argument. If the current state is the same, the server SHOULD skip fetching the records and return a result indicating there is no change (this is essentially like an ETag). Most types support the more sophisticated *getFooUpdates* call instead to allow for delta updates. However, for small collections of data that change infrequently, this might be used. If available, this argument is always optional.
-
-The response to `getFoos` is called `foos`. It has the following arguments:
-
-- **state**: `String`
-  A string representing the state on the server for **all** the data of this type. If the data changes, this string will change. It is used to get delta updates, if supported for the type.
-- **list**: `Foo[]|null`
-  An array of the Foo objects requested. This is the **empty array** if no objects were requested (and *ids* argument was passed with an empty array), or none were found (the user has no objects of this type, or none of the ids given were valid). If *sinceState* was supplied and it is identical to the current state, this property is `null` (the client already has up to date data so the server may skip returning it).
-- **notFound**: `String[]|null`
-  This array contains the ids passed to the method for records that do not exist, or `null` if all requested ids were found. It will always be `null` if the *ids* argument in the call was `null`.
-
-The following error may be returned instead of the `foos` response:
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getFooUpdates
-
-When the state of the set of Foo records changes on the server (whether due to creation, updates or deletion), the *state* property of the *foos* response will change. The *getFooUpdates* call allows a client to efficiently update the state of any its Foo cache to match the new state on the server. It takes the following arguments:
-
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *foos* response. The server will return the changes made since this state.
-- **maxChanges**: `Number|null`
-  The maximum number of Foo ids to return in the response. The server MAY choose to clamp this value to a particular maximum or set a maximum if none is given by the client. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting the *fooUpdates* response, the server will make an implicit call to *getFoos* with the *changed* property of the response as the *ids* argument. If `false` or `null`, no implicit call will be made.
-- **fetchRecordProperties**: `String[]|null`
-  If the *getFoos* method takes a *properties* argument, this argument is passed through on implicit calls (see the *fetchRecords* argument).
-
-The response to *getFooUpdates* is called *fooUpdates*. It has the following arguments:
-
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **hasMoreUpdates**: `Boolean`
-  If `true`, the client may call *getFooUpdates* again with the *newState* returned to get further updates. If `false`, *newState* is the current server state.
-- **changed**: `String[]`
-  An array of Foo ids for records which have been created or changed but not destroyed since the oldState.
-- **removed**: `String[]`
-  An array of Foo ids for records which have been destroyed since the old state.
-
-The *maxChanges* argument (and *hasMoreUpdates* response argument) is available for data types with potentially large amounts of data (i.e. those for which there is a *getFooList* method available for loading the data in pages).  If a *maxChanges* is supplied, or set automatically by the server, the server must try to limit the number of ids across *changed* and *removed* to the number given. If there are more changes than this between the client's state and the current server state, the update returned MUST take the client to an intermediate state, from which the client can continue to call *getFooUpdates* until it is fully up to date. The server MAY return more ids than the *maxChanges* total if this is required for it to be able to produce an update to an intermediate state, but it SHOULD try to keep it close to the maximum requested.
-
-If a Foo record has been modified AND deleted since the oldState, the server should just return the id in the *removed* response, but MAY return it in the changed response as well. If a Foo record has been created AND deleted since the oldState, the server should remove the Foo id from the response entirely, but MAY include it in the *removed* response, and optionally the *changed* response as well.
-
-The following errors may be returned instead of the *fooUpdates* response:
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old, or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its Foo cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setFoos
-
-Modifying the state of Foo objects on the server is done via the *setFoos* method. This encompasses creating, updating and destroying Foo records. This has two benefits:
-
-1. It allows the server to sort out ordering and dependencies that may exist if doing multiple operations at once (for example to ensure there is always a minimum number of a certain record type).
-2. Only a single call is required to make all changes to a particular type, so the *ifInState* requirement will not be invalidated by a previous method call in the same request.
-
-The *setFoos* method takes the following arguments:
-
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getFoos* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned. If `null`, the change will be applied to the current state.
-- **create**: `String[Foo]|null`
-  A map of *creation id* (an arbitrary string set by the client) to Foo objects (containing all properties except the id, unless otherwise stated in the specific documentation of the data type). If `null`, no objects will be created.
-- **update**: `String[Foo]|null`
-  A map of id to a Foo object. The object may omit any property; only properties that have changed need be included. If `null`, no objects will be updated.
-- **destroy**: `String[]|null`
-  A list of ids for Foo objects to permanently delete. If `null`, no objects will be deleted.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single record (e.g. update a *name* property but not a *count* property, if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-Some record objects may hold references to others (foreign keys). When records are created or modified, they may reference other records being created *in the same API request* by using the creation id prefixed with a `#`. The order of the method calls in the request by the client MUST be such that the record being referenced is created in the same or an earlier call. The server thus never has to look ahead. Instead, while processing a request (a series of method calls), the server MUST keep a simple map for the duration of the request of creation id to record id for each newly created record, so it can substitute in the correct value if necessary in later method calls.
-
-The response to *setFoos* is called *foosSet*. It has the following arguments:
-
-- **oldState**: `String|null`
-  The state string that would have been returned by *getFoos* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getFoos*.
-- **created**: `String[Foo]`
-  A map of the creation id to an object containing any **server-assigned** properties of the Foo object (including the id) for all successfully created records.
-- **updated**: `String[]`
-  A list of Foo ids for records that were successfully updated.
-- **destroyed**: `String[]`
-  A list of Foo ids for records that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each record that failed to be created. The possible errors are defined in the description of the method for specific data types.
-- **notUpdated**: `String[SetError]`
-  A map of Foo id to a SetError object for each record that failed to be updated. The possible errors are defined in the description of the method for specific data types.
-- **notDestroyed**: `String[SetError]`
-  A map of Foo id to a SetError object for each record that failed to be destroyed. The possible errors are defined in the description of the method for specific data types.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-Other properties may also be present on the object, as described in the relevant methods.
-
-The following errors may be returned instead of the `foosSet` response:
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an `ifInState` argument was supplied and it does not match the current state.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/authentication.mdwn b/server/protocols/jmap-draft/doc/specs/spec/authentication.mdwn
deleted file mode 100644
index 25e2c64..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/authentication.mdwn
+++ /dev/null
@@ -1,193 +0,0 @@
-## Authentication
-
-Before connecting to any JMAP service, the client must first gain an access token. It cannot just use a username/password directly. This allows the server to know (and show the user) which clients currently have access to the account, and to be able to revoke access individually.
-
-The server may support multiple different mechanisms for authenticating a user to gain the access token. It is expected that further types may be added in future extensions to the JMAP specification (for example [FIDO](https://fidoalliance.org/)).
-
-### Service autodiscovery
-<aside class="warning">
-Not implemented
-</aside>
-
-There are three common autodiscovery methods in use for internet protocols:
-
-- **DNS srv**
-  See [RFC6186](https://tools.ietf.org/html/rfc6186) and [RFC6764](https://tools.ietf.org/html/rfc6764)
-- **.well-known/`servicename`**
-  See [RFC5785](https://tools.ietf.org/html/rfc5785)
-- **autoconfig.example.com**/**autodiscover.example.com**
-  Used by [Thunderbird](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo) and Outlook.
-
-A JMAP-supporting email host for the domain `example.com` SHOULD publish a SRV record `_jmaps._tcp.example.com` which gives a hostname and port (usually port `443`).
-
-The authentication URL is `https://hostname/.well-known/jmap` (following any redirects).
-
-Other autodiscovery options using `autoconfig.example.com` or `autodiscover.example.com` may be added to a future version of JMAP to support clients which can't use SRV lookup.
-
-### Getting an access token
-<aside class="notice">
-Partially supported
-</aside>
-
-Authorization always starts with the client making a POST request to the authentication URL (found either via service autodiscovery or manual entry). The request MUST be of type `application/json` and specify an `Accept: application/json` header. The body of the request MUST be a single JSON object, encoded in UTF-8, with the following properties:
-
-- **username**: `String`
-  The username the client wishes to authenticate. This is normally the primary email address of the user.
-- **clientName**: `String`
-  The name of the client software. e.g. `Mozilla Thunderbird`.
-- **clientVersion**: `String`
-  Information to identify the version of the client.  This MUST change for any changed client code (e.g. a version control tag or counter for development software) and SHOULD sort lexically later for newer versions.
-- **deviceName**: `String`
-  A human-friendly string to identify the device making the request, e.g. "Joe Blogg's iPhone".
-
-The server may use the client/device information to help identify the login to the user in a login log or other security reporting. Although hopefully unnecessary, they may also be helpful for working around client bugs in the future.
-
-The server will respond with one of the following HTTP status codes:
-
-#### `200`: Success, but more authorization required.
-
-The response body will be a single JSON object with the following properties.
-
-- **continuationToken**: `String`
-  A token from the server to allow it to connect the next request with previous requests in the login process. This SHOULD be of limited time validity (e.g. 15 minutes from previous call).
-- **methods**: `String[]`
-  A list of the supported authentication methods to continue with authentication. This will often have only have one item in it. Allowed values are `"password"`, `"external"` and `"oauth"`. More options may be added in future extensions to JMAP.
-<aside class="warning">
-Support only `"password"`
-The client can also use OAuth by providing a header in the request such as this:
-`Authorization: Bearer JWT_TOKEN`
-</aside>
-- **prompt**: `String|null`
-  A message to display in the client to the user.
-
-This is the standard response to an initial request. Note, a server may return this even if the username is not actually active, to prevent enumeration. The client should then pick one of the *methods* from the list in the response to continue with authentication (if no methods supported by the client are in the list, it will not be able to log in to this account):
-
-- `"password"`: the client should prompt the user for a password. If the `prompt` property is non-null, this message should be displayed to the user to explain what input is required. The prompt MUST be treated as plain text, but the client SHOULD automatically hyperlink any URLs it finds in the text if a system browser is available. If `prompt == null`, the client SHOULD just show a generic password prompt message.
-- `"external"`: the user must do something out-of-band to authorize the app. The server SHOULD return a prompt string to display to the user to tell them what they need to do. Again, the client MUST treat the prompt as plain text, but SHOULD automatically hyperlink any URLs it finds if a system browser is available. The client MUST also offer a continue button (or similar) for the user to indicate to the client when they have completed the out-of-band authentication.
-- `"oauth"`: OAuth based authentication. For OAuth integration, see the docs of the service in question, since every service implements it slightly differently and the client must register with the service beforehand to use it. If using this method, an access token is obtained entirely through the OAuth mechanism. See the "Refetching URL endpoints" section below for how to obtain the URL endpoints after successfully authenticating using OAuth.
-
-If using `"password"` or `"external"`, the user will at some point indicate to the client to continue authentication. At this point the client submits a POST request to the same URL as before, with the body being a single JSON object with the following properties:
-
-- **token**: `String`
-  The *continuationToken* the server sent from the previous request.
-- **method**: `String`
-  The method chosen to continue authentication.
-- **password**: `String` (optional)
-  The password entered by the user (if `method == password`). The client SHOULD NOT store the password the user entered beyond what is required to submit it to the server in this step.
-
-The server will then return one of the same set of responses as before, which should be handled the same (for example, if 2FA is required, a `200` response may be returned again and a second password prompted for, with a `prompt` text explaining to the user that a one-time password has been SMSed to them).
-
-#### `201`: Authentication is complete, access token created.
-
-The response body will be a single JSON object with the following properties.
-
-- **versions**: `Number[]`
-  The list of supported JMAP-spec versions. If the client does not opt in to a particular version, then the lowest/oldest version in the list will be used by the server.
-- **extensions**: `String[Number[]]`
-  As described in the API Model, specific vendors may need to add custom extensions to the JMAP protocol, including new datatypes, extra properties on existing datatypes and extra methods. To ensure compability, these extensions must be explicitly opted into by the client.
-
-  The *extensions* property is a map of a name that uniquely identifies the extension (including a unique string identifying the vendor) to a list of version numbers supported for that extension. e.g. `"com.fastmail.message": [ 1 ]`.
-
-  A client may opt in to an extension by sending `"extension-name:extension-version"` in the `X-JMAP-Extensions` header of the request (or as specified for any future non-HTTP transport). It is up to the vendor to document exactly what the extension provides.
-
-  It is expected that this mechanism is mainly used for custom clients by the vendor for their own server. Where features are added that are more universally useful, it is hoped they will quickly be standardised in a future version of this spec, to maintain open compatibility between vendors.
-- **accessToken**: `String`
-  The secret token to be used by the client to authenticate all future JMAP requests. The client should keep this secure, preferably in an OS keychain or the like. Since tokens should not be reused across devices or clients, the client SHOULD NOT reveal this token to the user.
-- **api**: `String`
-  The URL to use for JMAP API requests.
-- **eventSource**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The URL to connect to for push events (see the Push section of this spec).
-- **upload**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The URL endpoint to use when uploading files (see the Upload section of this spec).
-- **download**: `String`
-  <aside class="warning">
-  Download endpoint does not handle name, it's not secured by any access protection and any authenticated user can get any attachment without restriction.</aside>
-  The URL endpoint to use when downloading files, in [RFC6570 URI Template](https://tools.ietf.org/html/rfc6570) (level 1) format. The URL MUST contain a variable called `blobId`. The URL SHOULD contain a variable called `name`. The client may use this template in combination with a blobId to download any binary data (files) referenced by other objects. Since a blob is not associated with a particular name, the template SHOULD allow a name to be substituted in as well; the server will return this as the filename if it sets a `Content-Disposition` header. To download the data the client MUST make an authenticated GET request (see below for how to authenticate requests) to the expanded URL, and then follow any redirects.
-
-URLs are returned only after logging in. This allows different URLs to be used for users located in different geographic datacentres within the same service.
-
-Note, if authentication is done via IP or mobile subscriber ID or some similar mechanism, a `201` response MAY be returned in response to the initial request (with just the username and client info).
-
-#### `400`: Malformed request
-
-The request is of the wrong content type, or does not contain data in the expected format. The client MUST NOT retry the same request.
-
-#### `401`: Authentication step failed
-
-Returned in response to a continuation request which failed (e.g. the password entered was not correct, or the out-of-band step was not completed successfully). The response body will be a single JSON object with the same properties as the `200` response.
-
-#### `403`: Restart authentication
-
-This normally means the continuation token has expired, but it can be returned for any other reason. The client MUST restart authentication (go back to sending the username and client info to the server).
-
-#### `429`: Rate limited
-
-Returned if the server is temporarily blocking this IP/client from authenticating. This may be due to too many failed password attempts, or detected username enumeration attempts, or any other reason. (Legitimate) clients should wait a while then try again.
-
-### Refetching URL endpoints
-
-A server MAY (although SHOULD NOT) move end points for any services other than authentication at any time. If a request to the API/file upload/event source endpoint returns a `404`, the client MUST refetch the URL endpoints. To do this, it should make an authenticated GET request to the authentication URL (see below for how to authenticate requests).
-
-For OAuth logins, this is how the URLs may be fetched initially as well.
-
-The server MUST respond with one of the following status codes:
-
-#### `200`: OK
-
-The request was successful. The response will be of type `application/json` and consists of a single JSON object containing the following properties:
-
-- **versions**: `Number[]`
-  The list of JMAP-spec versions supported by the account.
-- **extensions**: `String[Number[]]`
-  Map of extension names to version number of that extension (see above).
-- **api**: `String`
-  The URL to use for JMAP API requests.
-- **eventSource**: `String`
-  The URL to connect to for push events (see the Push section of this spec).
-- **upload**: `String`
-  The URL endpoint to use when uploading files (see the Upload section of this spec).
-- **download**: `String`
-  The URL endpoint to use when downloading files (see above).
-
-#### `401`: Unauthorized
-
-The `Authorization` header was missing or did not contain a valid token. Reauthenticate and then retry the request. There is no content in the response.
-
-#### `404`: Not Found
-
-The JMAP server is no longer here. There is no content in the response.
-
-#### `500`: Internal Server Error
-
-Something has gone wrong internally, and the server is in a broken state. Don't automatically retry. There is no content in the response.
-
-#### `503`: Service Unavailable
-
-The server is currently down. Try again later with exponential backoff. There is no content in the response.
-
-### Revoking an access token
-<aside class="notice">
-Tokens only have an expiration time of 15 minutes.
-</aside>
-
-The validity of an access token is determined by the server. It may be valid for a limited time only, or expire after a certain time of inactivity, or be valid indefinitely etc. If an access token expires, it MUST NOT be resurrected. The client MUST restart the authentication process to get a new access token.
-
-A client may revoke an access token at any time by making a DELETE HTTP request to the authentication URL (used to get the token in the first place), with the correct `Authorization` header (see below). The response from the server will be one of the following:
-
-`204`: Success (the access token has now been revoked).
-
-`401`: Failed due to due to no `Authorization` header, or `Authorization` header is not a valid access token.
-
-No content is returned in either case.
-
-For OAuth, see the provider's documentation on revoking access tokens.
-
-### Authenticating HTTP requests
-
-All HTTP requests other than to the authentication URL must be authenticated. To do this, the client MUST add an `Authorization` header to each request with the value being the access token. This applies to OAuth tokens too, but see the provider's documentation for the details of what value to use for the header.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/calendar.mdwn b/server/protocols/jmap-draft/doc/specs/spec/calendar.mdwn
deleted file mode 100644
index 1e78fc9..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/calendar.mdwn
+++ /dev/null
@@ -1,195 +0,0 @@
-## Calendars
-<aside class="warning">
-Not implemented
-</aside>
-
-A Calendar is a named collection of events. All events are associated with one, and only one, calendar.
-
-A **Calendar** object has the following properties:
-
-- **id**: `String`
-  The id of the calendar. This property is immutable.
-- **name**: `String`
-  The user-visible name of the calendar. This may be any UTF-8 string of at least 1 character in length and maximum 256 bytes in size.
-- **color**: `String`
-  Any valid CSS color value. The color to be used when displaying events associated with the calendar. The color SHOULD have sufficient contrast to be used as text on a white background.
-- **sortOrder**: `Number`
-  Defines the sort order of calendars when presented in the UI, so it is
-  consistent between devices. The number MUST be an integer in the range
-  0 <= sortOrder < 2^31.
-
-  A calendar with a lower order should be displayed before a calendar with
-  a higher order in any list of calendars in the client's UI. Calendars with
-  equal order should be sorted in alphabetical order by name. The sorting
-  should take into locale-specific character order convention.
-- **isVisible**: `Boolean`
-  Should the calendar's events be displayed to the user at the moment?
-- **mayReadFreeBusy**: `Boolean`
-  The user may read the free-busy information for this calendar. In JMAP
-  terms, this means the user may use this calendar as part of a filter in a
-  *getCalendarEventList* call, however unless `mayRead == true`, the events
-  returned for this calendar will only contain free-busy information, and be stripped of any other data.
-  This property MUST be `true` if *mayRead* is `true`.
-- **mayReadItems**: `Boolean`
-  The user may fetch the events in this calendar. In JMAP terms, this means
-  the user may use this calendar as part of a filter in a
-  *getCalendarEventList* call
-- **mayAddItems**: `Boolean`
-  The user may add events to this calendar. In JMAP terms, this means the
-  user may call *setCalendarEvents* to create new events in this calendar or
-  move existing events into this calendar from another calendar.
-  This property MUST be `false` if the account to which this calendar belongs
-  has the *isReadOnly* property set to `true`.
-- **mayModifyItems**: `Boolean`
-  The user may edit events in this calendar by calling *setCalendarEvents* with
-  the *update* argument referencing events in this collection.
-  This property MUST be `false` if the account to which this calendar belongs
-  has the *isReadOnly* property set to `true`.
-- **mayRemoveItems**: `Boolean`
-  The user may remove events from this calendar by calling *setCalendarEvents*
-  with the *destroy* argument referencing events in this collection, or by
-  updating their *calendarId* property to a different calendar.
-  This property MUST be `false` if the account to which this calendar belongs
-  has the *isReadOnly* property set to `true`.
-- **mayRename**: `Boolean`
-  The user may rename the calendar.
-  This property MUST be `false` if the account to which this calendar belongs
-  has the *isReadOnly* property set to `true`.
-- **mayDelete**: `Boolean`
-  The user may delete the calendar itself.
-  This property MUST be `false` if the account to which this calendar belongs
-  has the *isReadOnly* property set to `true`.
-
-
-### getCalendars
-
-Calendars can either be fetched explicitly by id, or all of them at once. To fetch calendars, make a call to `getCalendars`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The Account to fetch the calendars for. If `null`, the primary account is used.
-- **ids**: `String|null`
-  The ids of the calendars to fetch. If `null`, all calendars in the account are be fetched.
-
-The response to *getCalendars* is called *calendars*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **state**: `String`
-  A string representing the state on the server for **all** calendars. If a property of a calendar changes, or a new calendar is created, or a calendar is destroyed, this string will change. It is used to get delta updates.
-- **list**: `Calendar[]`
-  An array of the Calendar objects requested. This will be the **empty array** if the *ids* argument was the empty array, or contained only ids for calendars that could not be found.
-- **notFound**: `String[]|null`
-  This array contains the ids passed to the method for calendars that do not exist, or `null` if all requested ids were found. It will always be `null` if the *ids* argument in the call was `null`.
-
-The following errors may be returned instead of the *calendars* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getCalendarUpdates
-
-The *getCalendarUpdates* call allows a client to efficiently update the state of its cached calendars to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *calendars* response. The server will return the changes made since this state.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *calendarUpdates* response, an implicit call will be made to *getCalendars* with the *changed* property of the response as the *ids* argument. If `false` or `null`, no implicit call will be made.
-
-The response to *getCalendarUpdates* is called *calendarUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **changed**: `String[]`
-  An array of Calendar ids where a property of the calendar has changed between the old state and the new state, or the calendar has been created, and the calendar has not been destroyed.
-- **removed**: `String[]`
-  An array of Calendar ids for calendars which have been destroyed since the old state.
-
-If a calendar has been modified AND deleted since the oldState, the server should just return the id in the *removed* array, but MAY return it in the *changed* array as well. If a calendar has been created AND deleted since the oldState, the server SHOULD remove the calendar id from the response entirely, but MAY include it in the *removed* array, and optionally the *changed* array as well.
-
-The following errors may be returned instead of the `calendarUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old. The client MUST invalidate its Calendar cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setCalendars
-
-Modifying the state of Calendar objects on the server is done via the *setCalendars* method. This encompasses creating, updating and destroying Calendar records.
-
-The *setCalendars* method takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getCalendars* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned.
-- **create**: `String[Calendar]|null`
-  A map of *creation id* (an arbitrary string set by the client) to Calendar objects (containing all properties except the id).
-- **update**: `String[Calendar]|null`
-  A map of id to a Calendar object. The object may omit any property; only properties that have changed need be included.
-- **destroy**: `String[]|null`
-  A list of ids for Calendar objects to permanently delete.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single calendar (e.g. update the *name* property but not the *isVisible* property if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-The *mayXXX* properties are optional when creating a calendar. All default to `true`. If present, they MUST all be set to `true`. These properties are *read-only* to the client and may not be modified in an update call. Restrictions may only be set by the server, or when sharing calendars with other accounts (setting up sharing is not yet defined in this spec).
-
-A calendar MAY be deleted that is currently associated with one or more events. In this case, the events belonging to this calendar MUST also be deleted. Conceptually, this MUST happen prior to the calendar itself being deleted, and MUST generate a **push** event that modifies the state of the *CalendarEvent* type for the account, and has a *clientId* of `null`, to indicate that a change has been made to the event data not explicitly requested by the client.
-
-The response to *setCalendars* is called *calendarsSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by *getCalendars* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getCalendars*.
-- **created**: `String[Calendar]`
-  A map of the creation id to an object containing the **id** property for all successfully created calendars.
-- **updated**: `String[]`
-  A list of ids for groups that were successfully updated.
-- **destroyed**: `String[]`
-  A list of ids for calendars that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each calendar that failed to be created. The possible errors are defined in the description of the method for specific data types.
-- **notUpdated**: `String[SetError]`
-  A map of Calendar id to a SetError object for each calendar that failed to be updated. The possible errors are defined in the description of the method for specific data types.
-- **notDestroyed**: `String[SetError]`
-  A map of Calendar id to a SetError object for each calendar that failed to be destroyed. The possible errors are defined in the description of the method for specific data types.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-If any of the properties in a create or update are invalid (immutable and different to the current server value, wrong type, invalid value for the property – like a zero-length *name*), the server MUST reject the create/update with a SetError of type `invalidProperties`. The SetError object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-The following errors may be returned instead of the *calendarEventsSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an *ifInState* argument was supplied and it does not match the current state.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/calendarevent.mdwn b/server/protocols/jmap-draft/doc/specs/spec/calendarevent.mdwn
deleted file mode 100644
index a099011..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/calendarevent.mdwn
+++ /dev/null
@@ -1,305 +0,0 @@
-## Calendar Events
-<aside class="warning">
-Not implemented
-</aside>
-
-A CalendarEvent contains information about an event, or recurring series of events, that takes place at a particular time. The object is designed to be easily convertible to/from iCalendar format ([RFC 5545](https://tools.ietf.org/html/rfc5545)) for compatibility with existing calendaring systems.
-
-A **CalendarEvent** object has the following properties:
-
-- **id**: `String`
-  The id of the event. This property is immutable.
-- **calendarId**: `String`
-  The id of the calendar this event belongs to.
-- **summary**: `String`
-  A short summary of the event. This maps to the SUMMARY property in iCalendar.
-- **description**: `String`
-  A longer form description of the event. This is plain text, not HTML, but
-  a client *should* attempt to mark up URLs. This maps to the DESCRIPTION property in iCalendar.
-- **location**: `String`
-  Where the event is to take place. This maps to the LOCATION property in iCalendar.
-- **showAsFree**: `Boolean`
-  If true, the even should be ignored when calculating free/busy data for the
-  user. This maps to the TRANSP property in iCalendar (`false <=> OPAQUE`, `true <=> TRANSPARENT`).
-- **isAllDay**: `Boolean`
-  Is the event an all day event, such as a birthday or public holiday? This corresponds to the type (DATE or DATE-TIME) of the DTSTART property in iCalendar.
-- **start**: `LocalDate`
-  The date/time the event would start in the event's time zone. This corresponds to the DTSTART property in iCalendar.
-- **end**: `LocalDate`
-  The date/time the event would end in the event's time zone. This corresponds to the DTSTART/DURATION or DTEND property in iCalendar.
-- **startTimeZone**: `String|null`
-  The [Olsen Time Zone Database](http://www.iana.org/time-zones) name for the timezone the start of the event is actually in, or `null` for floating time. This corresponds to the TZID part of the DTSTART property; if the underlying iCalendar file does not use an Olsen name, the server SHOULD try to guess the correct time zone based on the VTIMEZONE information, or fallback to floating time.
-- **endTimeZone**: `String|null`
-  The [Olsen Time Zone Database](http://www.iana.org/time-zones) name for the timezone the end of the event is actually in, or `null` for floating time. This corresponds to the TZID part of the DTEND property (or DTSTART if not present); if the underlying iCalendar file does not use an Olsen name, the server SHOULD try to guess the correct time zone based on the VTIMEZONE information, or fallback to floating time.
-- **recurrence**: `Recurrence|null`
-  The recurrence rule for the event, or `null` if it does not recur. This corresponds to the RRULE property in iCalendar.
-- **inclusions**: `LocalDate[]|null`
-  List of extra **local** start times to recur on. The list MUST be sorted in date order (oldest first). This corresponds to the RDATE property in iCalendar.
-- **exceptions**: `LocalDate[null|CalendarEvent]|null`
-  An object mapping an occurrence start time (in **local time**, not UTC) to either:
-  - `null`: The occurrence has been deleted. This corresponds to the EXDATE property in iCalendar.
-  - `CalendarEvent`: A partial CalendarEvent object. Any properties in the object override the property of the same name for this occurrence. This corresponds to extra VEVENTs with RECURRENCE-IDs in iCalendar. Allowed properties are:
-    - summary
-    - description
-    - location
-    - showAsFree
-    - start
-    - end
-    - startTimeZone
-    - endTimeZone
-    - alerts
-    - organizer
-    - attendees
-- **alerts**: `Alert[]|null`
-  A list of alerts to display or send the user for this event. This maps to the VALARM property in iCalendar.
-- **organizer**: `Participant|null`
-  The organizer of the event. This maps to the ORGANIZER property in iCalendar
-- **attendees**: `Participant[]|null`
-  A list of attendees at the event. This maps to the ATTENDEE property in iCalendar.
-- **attachments**: `File[]|null`
-  A list of file attachments to the event. This maps to the ATTACH property in iCalendar.
-
-Conditions:
-
-- The *end* date MUST be equal to or after the *start* date when both are converted to UTC time (the event MUST NOT have a negative duration).
-- if *isAllDay* is `true`, the *start*/*end* properties MUST have a time component of `T00:00:00` and *startTimeZone*/*endTimeZone* properties MUST be `null`.
-- if *recurrence* is `null`, *inclusions* and *exceptions* MUST also be `null`.
-- either both organizer and attendees are `null`, or neither are.
-- any `null`able array/object property MUST be `null` rather than an empty array or object.
-
-A **Recurrence** object is a JSON object mapping of a RECUR value type in iCalendar. To make it easier to check if two recurrence rules are identical, optional properties MUST NOT be included if the value is the default. A Recurrence object has the following properties:
-
-- **frequency**: `String`
-  This MUST be one of the following values:
-  - `"yearly"`
-  - `"monthly"`
-  - `"weekly"`
-  - `"daily"`
-  - `"hourly"`
-  - `"minutely"`
-  - `"secondly"`
-  To convert from iCal, simply lower-case the FREQ part.
-- **interval**: `Number` (optional)
-  The INTERVAL part from iCal. Defaults to `1` if not present. This MUST NOT be included if the interval == 1, to ensure a canonical representation. If included, it MUST be an integer `x > 1`.
-- **firstDayOfWeek**: `Number` (optional)
-  The WKST part from iCal. Defaults to `1` (Monday) if not present. This MUST NOT be included if == 1 (Monday), to ensure a canonical representation. It MUST be an integer in the range (0,6), where SU => 0, TU => 2, WE => 3 etc.
-- **byDay**: `Number[]` (optional)
-  The BYDAY part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order. To convert from the iCal string format (e.g. SU,+1MO):
-  1. Convert SU/MO/TU… <-> 0/1/2…
-  2. If it has a number attached (+1, -2 etc.), add on 7 * the attached number.
-  e.g. `+1MO => 1 + (7 * +1) => 8`, `-2TH => 4 + (7 * -2) => -10`
-- **byDate**: `Number[]` (optional)
-  The BYMONTHDAY part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **byMonth**: `Number[]` (optional)
-  The BYMONTH part from iCal, but with Jan == 0, Feb == 1 etc. (Jan == 1 in iCal). The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **byYearDay**: `Number[]` (optional)
-  The BYYEARDAY part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **byWeekNo**: `Number[]` (optional)
-  The BYWEEKNO part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **byHour**: `Number[]` (optional)
-  The BYHOUR part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **byMinute**: `Number[]` (optional)
-  The BYMINUTE part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **bySecond**: `Number[]` (optional)
-  The BYSECOND part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **bySetPosition**: `Number[]` (optional)
-  The BYSETPOS part from iCal. The array MUST have at least one entry if included and MUST be sorted in ascending order.
-- **count**: `Number` (optional)
-  The COUNT part from iCal. This MUST NOT be included if an *until* property is specified.
-- **until**: `LocalDate` (optional)
-  The UNTIL part from iCal. This MUST NOT be included if a *count* property is specified.
-
-An **Alert** Object has the following properties:
-
-- **minutesBefore**: `Number`
-  The number of minutes before the start time of the event to show the alert. The number MAY be negative for an alert after the event start. Note, if the event is in floating time (including all-day events), the server SHOULD use the user's default time zone when determining the start time.
-- **type**: `String`
-  The value MUST be one of the following:
-  - `"email"` – the server will send an email to the user at the specified time. The format of this email is service-specific.
-  - `"alert"` – a message should be shown to the user on any client connected to this account at the specified time.
-
-A **Participant** Object has the following properties:
-
-- **name**: `String`
-  The name of the participant.
-- **email**: `String`
-  The email address of the participant.
-- **isYou**: `Boolean`
-  This is `true` if the participant is the logged in user (determining this is not defined and is server dependent).
-- **rsvp**: `String`
-  The value MUST be one of:
-  - `""`: no RSVP made.
-  - `"yes"`: the participant is attending
-  - `"maybe"`: the participant may be attending
-  - `"no"`: the participant is not attending
-
-A **File** Object has the following properties:
-
-- **blobId**: `String`
-  The id of the binary data.
-- **type**: `String|null`
-  The content-type of the attachment, if known.
-- **name**: `String|null`
-  The full file name, if known. e.g. "myworddocument.doc"
-- **size**: `Number|null`
-  The size, in bytes, of the attachment when fully decoded (i.e. the number of bytes in the file the user would download), if known.
-
-### getCalendarEvents
-
-CalendarEvents can only be fetched explicitly by id. To fetch events, make a call to `getCalendarEvents`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **ids**: `String[]`
-  An array of ids for the events to fetch.
-- **properties**: `String[]|null`
-  A list of properties to fetch for each event. If `null`, all properties will be fetched.
-
-The `id` property is always returned, regardless of whether it is in the list of requested properties. The possible values for `properties` can be found above in the description of the CalendarEvent object.
-
-The response to *getCalendarEvents* is called *calendarEvents*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **state**: `String`
-  A string encoding the current state on the server. This string will change
-  if any events change (that is, an event is created, updated or deleted). It can be passed to *getCalendarEventUpdates* to efficiently get the list of changes from the previous state.
-- **list**: `CalendarEvent[]`
-  An array of CalendarEvent objects for the requested event ids. This may not be in the same order as the ids were in the request.
-- **notFound**: `String[]|null`
-  An array of calendar event ids requested which could not be found, or `null` if all ids were found.
-
-The following errors may be returned instead of the *events* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getCalendarEventUpdates
-
-The *getCalendarEventUpdates* call allows a client to efficiently update the state of its cached calendar events to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *calendarEvents* response. The server will return the changes made since this state.
-- **maxChanges**: `Number|null`
-  The maximum number of CalendarEvent ids to return in the response. The server MAY choose to clamp this value to a particular maximum or set a maximum if none is given by the client. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *calendarEventUpdates* response, an implicit call will be made to *getCalendarEvents* with the *changed* property of the response as the *ids* argument, and the *fetchRecordProperties* argument as the *properties* argument. If `false` or `null`, no implicit call will be made.
-- **fetchRecordProperties**: `String[]|null`
-  Passed through as the *properties* argument to any implicit *getCalendarEvents* call.
-
-The response to *getCalendarEventUpdates* is called *calendarEventUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **hasMoreUpdates**: `Boolean`
-  If `true`, the client may call *getCalendarEventUpdates* again with the *newState* returned to get further updates. If `false`, *newState* is the current server state.
-- **changed**: `String[]`
-  An array of CalendarEvent ids where a property of the event has changed between the old state and the new state, or the event has been created, and the event has not been destroyed.
-- **removed**: `String[]`
-  An array of CalendarEvent ids for events which have been destroyed since the old state.
-
-If a *maxChanges* is supplied, or set automatically by the server, the server must try to limit the number of ids across *changed* and *removed* to the number given. If there are more changes than this between the client's state and the current server state, the update returned MUST take the client to an intermediate state, from which the client can continue to call *getCalendarEventUpdates* until it is fully up to date. The server MAY return more ids than the *maxChanges* total if this is required for it to be able to produce an update to an intermediate state, but it SHOULD try to keep it close to the maximum requested.
-
-If an event has been modified AND deleted since the oldState, the server should just return the id in the *removed* array, but MAY return it in the *changed* array as well. If an event has been created AND deleted since the oldState, the server SHOULD remove the event id from the response entirely, but MAY include it in the *removed* array, and optionally the *changed* array as well.
-
-The following errors may be returned instead of the `calendarEventUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old, or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its CalendarEvent cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setCalendarEvents
-
-Modifying the state of CalendarEvent objects on the server is done via the *setCalendarEvents* method. This encompasses creating, updating and destroying CalendarEvent records.
-
-The *setCalendarEvents* method takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getCalendarEvents* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned. If `null`, the change will be applied to the current state.
-- **create**: `String[CalendarEvent]|null`
-  A map of *creation id* (an arbitrary string set by the client) to CalendarEvent objects (containing all properties except the id).
-- **update**: `String[CalendarEvent]|null`
-  A map of id to a CalendarEvent object. The object may omit any property; only properties that have changed need be included.
-- **destroy**: `String[]|null`
-  A list of ids for CalendarEvent objects to permanently delete.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single event (e.g. update the *start* property but not the *startTimeZone* property if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-CalendarEvents reference a Calendar object. When events are created or modified, they may reference a calendar being created *in the same API request* by using the creation id prefixed with a `#`. The order of the method calls in the request by the client MUST be such that the calendar being referenced is created in an earlier call. The server thus never has to look ahead. Instead, while processing a request (a series of method calls), the server MUST keep a simple map for the duration of the request of creation id to Calendar id for each newly created calendar, so it can substitute in the correct value if necessary in later method calls.
-
-To add new attachments, the file must first be uploaded using the standard upload mechanism (see the File Uploads section of this spec). This will give the client a valid blobId/size/type to use.
-
-When an event is created, updated or destroyed, the server MUST also ensure the following:
-
-- Any alerts are scheduled/cancelled correctly..
-- If `organizer.isYou == true`:
-  - If an event is created with attendees, send a REQUEST iMIP email with the
-    event as an ICS attachment to all attendees with `isYou == false`.
-  - When an event is updated, email all attendees with `isYou == false` the
-    change with an appropriate iMIP email.
-  - When an event is destroyed, if it is in the future, then email all
-    attendees with `isYou == false` to inform them that the event has been cancelled. If it is in the past, the server SHOULD NOT send a message.
-- If `organizer.isYou != true` and one of the attendees of the event has `isYou == true`, then changing the RSVP status of this attendee MUST send the appropriate email to the organizer to inform them of the change in status, as per the iMIP standard.
-
-Note, for compatibility with CalDAV clients, when updating an attendee, the server SHOULD maintain any ROLE, CUTYPE, MEMBER, DELEGATED-TO, DELEGATED-FROM, SENT-BY etc. parameters currently in the iCalendar object for that attendee, even though this API does not currently care about it.
-
-The response to *setCalendarEvents* is called *calendarEventsSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by *getCalendarEvents* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getCalendarEvents*.
-- **created**: `String[CalendarEvent]`
-  A map of the creation id to an object containing the **id** property for all successfully created events
-- **updated**: `String[]`
-  A list of ids for events that were successfully updated.
-- **destroyed**: `String[]`
-  A list of ids for events that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each event that failed to be created. The possible errors are defined in the description of the method for specific data types.
-- **notUpdated**: `String[SetError]`
-  A map of CalendarEvent id to a SetError object for each event that failed to be updated. The possible errors are defined in the description of the method for specific data types.
-- **notDestroyed**: `String[SetError]`
-  A map of CalendarEvent id to a SetError object for each event that failed to be destroyed. The possible errors are defined in the description of the method for specific data types.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-If any of the properties in a create or update are invalid (immutable and different to the current server value, wrong type, invalid value for the property – like a *calendarId* for a non-existent calendar), the server MUST reject the create/update with a SetError of type `invalidProperties`. The SetError object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-The following errors may be returned instead of the *calendarEventsSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an *ifInState* argument was supplied and it does not match the current state.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/calendareventlist.mdwn b/server/protocols/jmap-draft/doc/specs/spec/calendareventlist.mdwn
deleted file mode 100644
index 0ddf1d5..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/calendareventlist.mdwn
+++ /dev/null
@@ -1,97 +0,0 @@
-## CalendarEventLists
-<aside class="warning">
-Not implemented
-</aside>
-
-A **CalendarEventList** is a query on the set of events in a user's calendars. The client can optionally also fetch the events.
-
-### getCalendarEventList
-
-To fetch a calendar event list, make a call to *getCalendarEventList*. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **filter**: `FilterCondition|FilterOperator|null`
-  Determines the set of events returned in the results. See the "Filtering" section below for allowed values and semantics.
-- **position**: `Number|null`
-  The 0-based index of the first result in the list to return, presumed `0` if `null`. If a negative value is given, the call MUST be rejected with an `invalidArguments` error.
-- **limit**: `Number|null`
-  The maximum number of results to return. If `null`, no limit presumed. The server MAY choose to enforce a maximum `limit` argument. In this case, if a greater value is given (or if it is `null`), the limit should be clamped to the maximum; since the total number of results in the list is returned, the client can determine if it has received all the results. If a negative value is given, the call MUST be rejected with an `invalidArguments` error.
-- **fetchCalendarEvents**: `Boolean|null`
-  If `true` then after outputting a *calendarEventList* response, an implicit call will be made to *getCalendarEvents* with the `calendarEventIds` array in the response as the *ids* argument. If `false` or `null`, no implicit call will be made.
-
-#### Filtering
-
-A **FilterOperator** object has the following properties:
-
-- **operator**: `String`
-  This MUST be one of the following strings: "AND"/"OR"/"NOT":
-  - **AND**: all of the conditions must match for the filter to match.
-  - **OR**: at least one of the conditions must match for the filter to match.
-  - **NOT**: none of the conditions must match for the filter to match.
-- **conditions**: `(FilterCondition|FilterOperator)[]`
-  The conditions to evaluate against each event.
-
-A **FilterCondition** object has the following properties:
-
-- **inCalendars**: `String[]|null`
-  A list of calendar ids. An event must be in ANY of these calendars to match the condition.
-- **after**: `Date|null`
-  The end of the event, or any recurrence of the event, in UTC time must be after this date to match the condition.
-- **before**: `Date|null`
-  The start of the event, or any recurrence of the event, in UTC time must be before this date to match the condition.
-- **text**: `String|null`
-  Looks for the text in the *summary*, *description*, *location*, *organizer*, *attendees* properties of the event or any recurrence of the event (matching either name or email in the organizer/attendee case).
-- **summary**: `String|null`
-  Looks for the text in the *summary* property of the event, or the overridden *summary* property of a recurrence.
-- **description**: `String|null`
-  Looks for the text in the *description* property of the event, or the overridden *description* property of a recurrence.
-- **location**: `String|null`
-  Looks for the text in the *location* property of the event, or the overridden *location* property of a recurrence.
-- **organizer**: `String|null`
-  Looks for the text in the name or email fields of the *organizer* property of the event, or the overridden *organizer* property of a recurrence.
-- **attendee**: `String|null`
-  Looks for the text in the name or email of any item in the *attendees* property of the event, or the overridden *attendees* property of a recurrence.
-
-If zero properties are specified on the FilterCondition, the condition MUST always evaluate to `true`. If multiple properties are specified, ALL must apply for the condition to be `true` (it is equivalent to splitting the object into one-property conditions and making them all the child of an AND filter operator).
-
-The exact semantics for matching `String` fields is **deliberately not defined** to allow for flexibility in indexing implementation, subject to the following:
-
-- Text SHOULD be matched in a case-insensitive manner.
-- Text contained in either (but matched) single or double quotes SHOULD be treated as a **phrase search**, that is a match is required for that exact sequence of words, excluding the surrounding quotation marks. Use `\"`, `\'` and `\\` to match a literal `"`, `'` and `\` respectively in a phrase.
-- Outside of a phrase, white-space SHOULD be treated as dividing separate tokens that may be searched for separately in the event, but MUST all be present for the event to match the filter.
-- Tokens MAY be matched on a whole-word basis using stemming (so for example a text search for `bus` would match "buses" but not "business").
-
-#### Sorting
-
-Results MUST be sorted in a stable order so the client can load the full list in sections. The exact ordering to use is server dependent.
-
-#### Windowing
-
-To paginate the results the client MAY supply a *position* argument: this is the 0-based index of the first result to return in the list of events after filtering and sorting. If the index is greater than or equal to the total number of events in the list, then there are no results to return, but this DOES NOT generate an error. If `null`, this defaults to `0`.
-
-#### Response
-
-The response to a call to *getCalendarEventList* is called *calendarEventList*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **filter**: `FilterCondition|FilterOperator|null`
-  The filter of the event list. Echoed back from the call.
-- **state**: `String`
-  A string encoding the current state on the server. This string will change
-  if the results of the event list MAY have changed (for example, there has been a change to the state of the set of CalendarEvents; it does not guarantee that anything in the list has changed).
-- **position**: `Number`
-  The 0-based index of the first result in the `calendarEventIds` array within the complete list.
-- **total**: `Number`
-  The total number of events in the list (given the *filter*).
-- **calendarEventIds**: `String[]`
-  The list of CalendarEvent ids for each event in the list after filtering and sorting, starting at the index given by the *position* argument of this response, and continuing until it hits the end of the list or reaches the `limit` number of ids. Note, that recurrences are not returned separately in any way; the event only occurs once in the calendarEventIds array no matter how many times it recurs.
-
-The following errors may be returned instead of the `calendarEventList` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid account, but does not contain any calendar data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/contact.mdwn b/server/protocols/jmap-draft/doc/specs/spec/contact.mdwn
deleted file mode 100644
index 914a166..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/contact.mdwn
+++ /dev/null
@@ -1,246 +0,0 @@
-## Contacts
-<aside class="warning">
-Not implemented
-</aside>
-
-A **Contact** object stores information about a person or company. It has the following properties:
-
-- **id**: `String`
-  The id of the contact. This property is immutable.
-- **isFlagged**: `Boolean`
-  Is the contact flagged (marked as important in some way by the user)?
-- **avatar**: `File|null`
-  A file containing an image to be used to represent this contact.
-- **prefix**: `String`
-  The honorific title of the contact, e.g. "Mr", "Ms", "Dr", etc.
-- **firstName**: `String`
-  The first name(s) of a contact.
-- **lastName**: `String`
-  The last name of a contact.
-- **suffix**: `String`
-  The honorific suffix of the contact, e.g. "B.A.", "Esq." etc.
-- **nickname**: `String`
-  The nickname of the contact.
-- **birthday**: `String`
-  The person's birth date in the form `"YYYY-MM-DD"` (any part may be all `0`s for unknown).
-- **anniversary**: `String`
-  The person's anniversary date in the form `"YYYY-MM-DD"` (any part may be all `0`s for unknown).
-- **company**: `String`
-  The company for which the person works.
-- **department**: `String`
-  The department within the company for which the person works.
-- **jobTitle**: `String`
-  The job title of the person.
-- **emails**: `ContactInformation[]`
-  An array of ContactInformation objects where the values are email addresses. Types are:
-  - `"personal"` The address is for emailing the person in a personal context.
-  - `"work"` The address is for emailing the person in a professional context.
-  - `"other"` The address is for some other purpose. A *label* property MAY be included to display next to the address to help the user identify its purpose.
-- **phones**: `ContactInformation[]`
-  An array of ContactInformation objects where the values are phone numbers. Types are:
-  - `"home"` The number is for contacting the person at their residence.
-  - `"work"` The number is for contacting the person at their workplace.
-  - `"mobile"` The number is for contacting the person regardless of location.
-  - `"fax"` The number is for sending faxes to the contact.
-  - `"pager"` The number is for a pager or beeper associated with the contact.
-  - `"other"` The number is for some other purpose. A *label* property MAY be included to display next to the number to help the user identify its purpose.
-- **online**: `ContactInformation[]`
-  An array of ContactInformation objects where the values are URIs or usernames associated with the person for online services.
-  Types are:
-  - `"uri"` The value is a URI, e.g. a website link.
-  - `"username"` The value is a username associated with the person (e.g. for Twitter, or Facebook, or an IM client). A *label* property SHOULD be included to identify what service this is for. For compatibility between clients, this label SHOULD be the canonical service name, including capitalisation. e.g. `"Twitter"`, `"Facebook"`, `"Skype"`, `"GitHub"`, `"XMPP"`.
-  - `"other"` The value is something else not covered by the above categories. A *label* property MAY be included to display next to the number to help the user identify its purpose.
-- **addresses**: `Address[]`
-  An array of Address objects, containing physical locations associated with the person.
-  Types are:
-  - `"home"` An address of a residence associated with the person.
-  - `"work"` An address of a workplace associated with the person.
-  - `"billing"` An address to be used with billing associated with the person.
-  - `"postal"` An address to be used for delivering physical items to the person.
-  - `"other"` An address not covered by the above categories.
-- **notes**: `String`
-  Arbitrary notes about the contact.
-
-Note, none of these properties have a `null`able type. If the specific information is not known for a contact, the empty string or empty array should be used as appropriate, or in the case of birthday the string `"0000-00-00"`.
-
-A **ContactInformation** object has the following properties:
-
-- **type**: `String`
-  Specifies the context of the contact information. This MUST be taken from the set of values allowed depending on whether this is part of the *phones*, *emails* or *online* property (see above).
-- **label**: `String|null`
-  A label describing the value in more detail, especially if `type === "other"` (but MAY be included with any type).
-- **value**: `String`
-  The actual contact information, e.g. the email address or phone number.
-- **isDefault**: `Boolean`
-  Whether this contact is the default for its type (SHOULD only be one per type, but some backends will allow multiple defaults)
-
-An **Address** object has the following properties:
-
-- **type**: `String`
-  Specifies the context of the contact information. This MUST be taken from the set of values allowed depending on whether this is part of the *phones*, *emails* or *online* property (see above).
-- **label**: `String|null`
-  A label describing the value in more detail, especially if `type === "other"` (but MAY be included with any type).
-- **street**: `String`
-  The street address. This MAY be multiple lines; newlines MUST be preserved.
-- **locality**: `String`
-  The city, town, village, post town, or other locality within which the street address may be found.
-- **region**: `String`
-  The province, such as a state, county, or canton within which the locality may be found.
-- **postcode**: `String`
-  The postal code, post code, ZIP code or other short code associated with the address by the relevant country's postal system.
-- **country**: `String`
-  The country name.
-- **isDefault**: `Boolean`
-  Whether this contact is the default for its type (SHOULD only be one per type, but some backends will allow multiple defaults)
-
-A **File** Object has the following properties:
-
-- **blobId**: `String`
-  The id of the binary data.
-- **type**: `String|null`
-  The content-type of the file, if known.
-- **name**: `String|null`
-  The full file name, if known. e.g. "myface.png"
-- **size**: `Number|null`
-  The size, in bytes, of the file when fully decoded (i.e. the number of bytes in the file the user would download), if known.
-
-### getContacts
-
-Contacts can either be fetched explicitly by id, or all of them at once. To fetch contacts, make a call to `getContacts`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The Account to fetch the contacts for. If `null`, the primary account is used.
-- **ids**: `String|null`
-  The ids of the contacts to fetch. If `null`, all contacts in the account are be fetched.
-- **properties**: `String[]|null`
-  If supplied, only the properties listed in the array will be returned for each contact. If `null`, all properties are returned. The id of the contact will **always** be returned, even if not explicitly requested.
-
-The response to *getContacts* is called *contacts*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **state**: `String`
-  A string representing the state on the server for **all** contacts. If the state of a contact changes, or a new contact is created, or a contact is destroyed, this string will change. It is used to get delta updates.
-- **list**: `Contact[]`
-  An array of the Contact objects requested. This will be the **empty array** if the *ids* argument was the empty array, or contained only ids for contacts that could not be found.
-- **notFound**: `String[]|null`
-  This array contains the ids passed to the method for contacts that do not exist, or `null` if all requested ids were found. It will always be `null` if the *ids* argument in the call was `null`.
-
-The following errors may be returned instead of the *contacts* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not contain any contact data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getContactUpdates
-
-The *getContactUpdates* call allows a client to efficiently update the state of its cached contacts to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *contacts* response. The server will return the changes made since this state.
-- **maxChanges**: `Number|null`
-  The maximum number of Contact ids to return in the response. The server MAY choose to clamp this value to a particular maximum or set a maximum if none is given by the client. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *contactUpdates* response, an implicit call will be made to *getContacts* with the *changed* property of the response as the *ids* argument, and the *fetchRecordProperties* argument as the *properties* argument. If `false` or `null`, no implicit call is made.
-- **fetchRecordProperties**: `String[]|null`
-  Passed through as the *properties* argument to any implicit *getContacts* call.
-
-The response to *getContactUpdates* is called *contactUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **hasMoreUpdates**: `Boolean`
-  If `true`, the client may call *getContactUpdates* again with the *newState* returned to get further updates. If `false`, *newState* is the current server state.
-- **changed**: `String[]`
-  An array of Contact ids where a property of the contact has changed between the old state and the new state, or the contact has been created, and the contact has not been destroyed.
-- **removed**: `String[]`
-  An array of Contact ids for contacts which have been destroyed since the old state.
-
-If a *maxChanges* is supplied, or set automatically by the server, the server must try to limit the number of ids across *changed* and *removed* to the number given. If there are more changes than this between the client's state and the current server state, the update returned MUST take the client to an intermediate state, from which the client can continue to call *getContactUpdates* until it is fully up to date. The server MAY return more ids than the *maxChanges* total if this is required for it to be able to produce an update to an intermediate state, but it SHOULD try to keep it close to the maximum requested.
-
-If a contact has been modified AND deleted since the oldState, the server should just return the id in the *removed* array, but MAY return it in the *changed* array as well. If a contact has been created AND deleted since the oldState, the server SHOULD remove the contact id from the response entirely, but MAY include it in the *removed* array, and optionally the *changed* array as well.
-
-The following errors may be returned instead of the `contactUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not contain any contacts data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old, or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its Contact cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setContacts
-
-Modifying the state of Contact objects on the server is done via the *setContacts* method. This encompasses creating, updating and destroying Contact records.
-
-The *setContacts* method takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getContacts* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned. If `null`, the change will be applied to the current state.
-- **create**: `String[Contact]|null`
-  A map of *creation id* (an arbitrary string set by the client) to Contact objects (containing all properties except the id).
-- **update**: `String[Contact]|null`
-  A map of id to a Contact object. The object may omit any property; only properties that have changed need be included.
-- **destroy**: `String[]|null`
-  A list of ids for Contact objects to permanently delete.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single contact (e.g. update the *firstName* property but not the *lastName* property if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-To set a new avatar, the file must first be uploaded using the standard upload mechanism (see the File Uploads section of this spec). This will give the client a valid blobId/size/type to use. The server SHOULD reject attempts to set a file that is not a recognised image type as the avatar for a contact.
-
-The response to *setContacts* is called *contactsSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by *getContacts* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getContacts*.
-- **created**: `String[Contact]`
-  A map of the creation id to an object containing the **id** property for all successfully created contacts.
-- **updated**: `String[]`
-  A list of ids for contacts that were successfully updated.
-- **destroyed**: `String[]`
-  A list of ids for contacts that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each contact that failed to be created. The possible errors are defined in the description of the method for specific data types.
-- **notUpdated**: `String[SetError]`
-  A map of Contact id to a SetError object for each contact that failed to be updated. The possible errors are defined in the description of the method for specific data types.
-- **notDestroyed**: `String[SetError]`
-  A map of Contact id to a SetError object for each contact that failed to be destroyed. The possible errors are defined in the description of the method for specific data types.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-Other properties may also be present on the object, as described in the relevant methods.
-
-The following errors may be returned instead of the *contactsSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not contain any contacts data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an *ifInState* argument was supplied and it does not match the current state.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/contactgroup.mdwn b/server/protocols/jmap-draft/doc/specs/spec/contactgroup.mdwn
deleted file mode 100644
index 2e0c13a..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/contactgroup.mdwn
+++ /dev/null
@@ -1,138 +0,0 @@
-## Contact Groups
-<aside class="warning">
-Not implemented
-</aside>
-
-A **Contact Group** represents a named set of contacts. It has the following properties:
-
-- **id**: `String`
-  The id of the group. This property is immutable.
-- **name**: `String`
-  The user-visible name for the group, e.g. "Friends". This may be any UTF-8 string of at least 1 character in length and maximum 256 bytes in size. The same name may be used by two different groups.
-- **contactIds**: `String[]`
-  The ids of the contacts in the group. This must be returned in the same order given by the client when the property is set.
-
-### getContactGroups
-
-Contact Groups can either be fetched explicitly by id, or all of them at once. To fetch contact groups, make a call to `getContactGroups`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The Account to fetch the groups for. If `null`, the primary account is used.
-- **ids**: `String[]|null`
-  The ids of the groups to fetch. If `null`, all contact groups in the account are be fetched.
-
-The response to *getContactGroups* is called *contactGroups*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **state**: `String`
-  A string representing the state on the server for **all** contact groups. If the name of a group changes, or a new group is created, or a group is destroyed, this string will change. It is used to get delta updates.
-- **list**: `ContactGroup[]`
-  An array of the ContactGroup objects requested. This will be the **empty array** if the *ids* argument was the empty array, or contained only ids for contact groups that could not be found.
-- **notFound**: `String[]|null`
-  This array contains the ids passed to the method for groups that do not exist, or `null` if all requested ids were found. It will always be `null` if the *ids* argument in the call was `null`.
-
-The following errors may be returned instead of the *contactGroups* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not contain any contact data.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getContactGroupUpdates
-
-The *getContactGroupUpdates* call allows a client to efficiently update the state of its cached contacts to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *contactGroups* response. The server will return the changes made since this state.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *contactGroupUpdates* response, an implicit call will be made to *getContactGroups* with the *changed* property of the response as the *ids* argument. If `false` or `null`, no implicit call will be made.
-
-The response to *getContactGroupUpdates* is called *contactGroupUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **changed**: `String[]`
-  An array of ContactGroup ids where a property of the group has changed between the old state and the new state, or the group has been created, and the group has not been destroyed.
-- **removed**: `String[]`
-  An array of ContactGroup ids for groups which have been destroyed since the old state.
-
-If a group has been modified AND deleted since the oldState, the server should just return the id in the *removed* array, but MAY return it in the *changed* array as well. If a group has been created AND deleted since the oldState, the server SHOULD remove the group id from the response entirely, but MAY include it in the *removed* array, and optionally the *changed* array as well.
-
-The following errors may be returned instead of the `contactGroupUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not contain any contacts data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old. The client MUST invalidate its ContactGroup cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setContactGroups
-
-Modifying the state of ContactGroup objects on the server is done via the *setContactGroups* method. This encompasses creating, updating and destroying ContactGroup records.
-
-The *setContactGroups* method takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getContactGroups* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned. If `null`, the change will be applied to the current state.
-- **create**: `String[ContactGroup]|null`
-  A map of *creation id* (an arbitrary string set by the client) to ContactGroup objects (containing all properties except the id).
-- **update**: `String[ContactGroup]|null`
-  A map of id to a ContactGroup object. The object may omit any property; only properties that have changed need be included.
-- **destroy**: `String[]|null`
-  A list of ids for ContactGroup objects to permanently delete.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single group (e.g. update the *name* property but not the *contactIds* property if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-If any of the properties in a create or update are invalid (immutable and different to the current server value, wrong type, non-existent contactId value in contactsIds list), the server MUST reject the create/update with a SetError of type `invalidProperties`. The SetError object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-Contacts Groups reference Contacts. When groups are created or modified, they may reference contacts being created *in the same API request* by using the creation id prefixed with a `#`. The order of the method calls in the request by the client MUST be such that the contact being referenced is created in an earlier call. The server thus never has to look ahead. Instead, while processing a request (a series of method calls), the server MUST keep a simple map for the duration of the request of creation id to Contact id for each newly created contact, so it can substitute in the correct value if necessary in later method calls.
-
-The response to *setContactGroups* is called *contactGroupsSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by *getContactGroups* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getContactGroups*.
-- **created**: `String[Contact]`
-  A map of the creation id to an object containing the **id** property for all successfully created groups.
-- **updated**: `String[]`
-  A list of ids for groups that were successfully updated.
-- **destroyed**: `String[]`
-  A list of ids for groups that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each group that failed to be created. The possible errors are defined in the description of the method for specific data types.
-- **notUpdated**: `String[SetError]`
-  A map of ContactGroup id to a SetError object for each group that failed to be updated. The possible errors are defined in the description of the method for specific data types.
-- **notDestroyed**: `String[SetError]`
-  A map of ContactGroup id to a SetError object for each group that failed to be destroyed. The possible errors are defined in the description of the method for specific data types.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-The following errors may be returned instead of the *contactGroupsSet* response:
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an `ifInState` argument was supplied and it does not match the current state.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/contactlist.mdwn b/server/protocols/jmap-draft/doc/specs/spec/contactlist.mdwn
deleted file mode 100644
index 13504fc..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/contactlist.mdwn
+++ /dev/null
@@ -1,114 +0,0 @@
-## ContactLists
-<aside class="warning">
-Not implemented
-</aside>
-
-A **ContactList** is a query on the set of contacts in a user's contacts. The client can optionally also fetch the contacts.
-
-### getContactList
-
-To fetch a contact list, make a call to *getContactList*. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **filter**: `FilterCondition|FilterOperator|null`
-  Determines the set of contacts returned in the results. See the "Filtering" section below for allowed values and semantics.
-- **position**: `Number|null`
-  The 0-based index of the first result in the list to return, presumed `0` if `null`. If a negative value is given, the call MUST be rejected with an `invalidArguments` error.
-- **limit**: `Number|null`
-  The maximum number of results to return. If `null`, no limit presumed. The server MAY choose to enforce a maximum `limit` argument. In this case, if a greater value is given (or if it is `null`), the limit should be clamped to the maximum; since the total number of results in the list is returned, the client can determine if it has received all the results. If a negative value is given, the call MUST be rejected with an `invalidArguments` error.
-- **fetchContacts**: `Boolean|null`
-  If `true` then after outputting a *contactList* response, an implicit call will be made to *getContacts* with the `contactIds` array in the response as the *ids* argument. If `false` or `null`, no implicit call will be made.
-
-#### Filtering
-
-A **FilterOperator** object has the following properties:
-
-- **operator**: `String`
-  This MUST be one of the following strings: "AND"/"OR"/"NOT":
-  - **AND**: all of the conditions must match for the filter to match.
-  - **OR**: at least one of the conditions must match for the filter to match.
-  - **NOT**: none of the conditions must match for the filter to match.
-- **conditions**: `(FilterCondition|FilterOperator)[]`
-  The conditions to evaluate against each contact.
-
-A **FilterCondition** object has the following properties:
-
-- **inContactGroup**: `String[]|null`
-  A list of contact group ids. A contact must be in ANY of these groups to match the condition.
-- **isFlagged**: `Boolean|null`
-  The `isFlagged` property of the contact must be identical to the value given to match the condition.
-- **text**: `String|null`
-  Equivalent to ORing together a FilterCondition for each of the `String` typed conditions (prefix, firstName, etc.).
-- **prefix**: `String`
-  Looks for the text in the *prefix* property of the contact.
-- **firstName**: `String`
-  Looks for the text in the *firstName* property of the contact.
-- **lastName**: `String`
-  Looks for the text in the *lastName* property of the contact.
-- **suffix**: `String`
-  Looks for the text in the *suffix* property of the contact.
-- **nickname**: `String`
-  Looks for the text in the *nickname* property of the contact.
-- **company**: `String`
-  Looks for the text in the *company* property of the contact.
-- **department**: `String`
-  Looks for the text in the *department* property of the contact.
-- **jobTitle**: `String`
-  Looks for the text in the *jobTitle* property of the contact.
-- **email**: `String`
-  Looks for the text in the *value* property of any object in the *emails*
-  property of the contact.
-- **phone**: `String`
-  Looks for the text in the *value* property of any object in the *phones*
-  property of the contact.
-- **online**: `String`
-  Looks for the text in the *value* property of any object in the *online*
-  property of the contact.
-- **address**: `String`
-  Looks for the text in the *street*, *locality*, *region*, *postcode* or *country* property of any object in the *addresses* property of the contact.
-- **notes**: `String`
-  Looks for the text in the *notes* property of the contact.
-
-If zero properties are specified on the FilterCondition, the condition MUST always evaluate to `true`. If multiple properties are specified, ALL must apply for the condition to be `true` (it is equivalent to splitting the object into one-property conditions and making them all the child of an AND filter operator).
-
-The exact semantics for matching `String` fields is **deliberately not defined** to allow for flexibility in indexing implementation, subject to the following:
-
-- Text SHOULD be matched in a case-insensitive manner.
-- Text contained in either (but matched) single or double quotes SHOULD be treated as a **phrase search**, that is a match is required for that exact sequence of words, excluding the surrounding quotation marks. Use `\"`, `\'` and `\\` to match a literal `"`, `'` and `\` respectively in a phrase.
-- Outside of a phrase, white-space SHOULD be treated as dividing separate tokens that may be searched for separately in the contact, but MUST all be present for the contact to match the filter.
-- Tokens MAY be matched on a whole-word basis using stemming (so for example a text search for `bus` would match "buses" but not "business").
-
-#### Sorting
-
-Results MUST be sorted in a stable order so the client can load the full list in sections. The exact ordering to use is server dependent.
-
-#### Windowing
-
-To paginate the results the client MAY supply a *position* argument: this is the 0-based index of the first result to return in the list of contacts after filtering and sorting. If the index is greater than or equal to the total number of contacts in the list, then there are no results to return, but this DOES NOT generate an error. If `null`, this defaults to `0`.
-
-#### Response
-
-The response to a call to *getContactList* is called *contactList*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **filter**: `FilterCondition|FilterOperator|null`
-  The filter of the contact list. Echoed back from the call.
-- **state**: `String`
-  A string encoding the current state on the server. This string will change
-  if the results of the contact list MAY have changed (for example, there has been a change to the state of the set of Contacts; it does not guarantee that anything in the list has changed).
-- **position**: `Number`
-  The 0-based index of the first result in the `contactIds` array within the complete list.
-- **total**: `Number`
-  The total number of contacts in the list (given the *filter*).
-- **contactIds**: `String[]`
-  The list of Contact ids for each contact in the list after filtering and sorting, starting at the index given by the *position* argument of this response, and continuing until it hits the end of the list or reaches the `limit` number of ids.
-
-The following errors may be returned instead of the `contactList` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoContacts`: Returned if the *accountId* given corresponds to a valid account, but does not support storing contact data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/datamodel.mdwn b/server/protocols/jmap-draft/doc/specs/spec/datamodel.mdwn
deleted file mode 100644
index 7ce1826..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/datamodel.mdwn
+++ /dev/null
@@ -1,39 +0,0 @@
-## Data model overview
-
-### Accounts
-<aside class="warning">
-Not implemented
-</aside>
-
-All data belongs to an account. An account may provide access to mail, contacts and/or calendars. Most operations are isolated to a single account; there are a few explicit operations to copy data between them. A single login may provide access to multiple accounts, for example if another user is sharing data with the user.
-
-### Ids
-<aside class="notice">
-Work in progress. Unique message id is generated by concatenate the username, the mailboxId and the uid.
-</aside>
-
-All ids for messages, contacts, etc. are assigned by the server and are immutable. They MUST be unique among all records of the **same type** within the **same account**. Ids may clash across accounts, or for two records of different types within the same account.
-
-### Mail
-
-As for all data types, each message has a unique, immutable id. This id does not change if the message changes mailboxes. A mailbox is a named, arbitrary set of emails. For compatibility with IMAP, a message MUST always belong to at least one mailbox at any time.
-
-Each message has 4 mutable boolean "flags" stored with it, each with a direct
-equivalent in IMAP. In JMAP these flags are called:
-
-* **isUnread**: Has the email not yet been read?
-* **isFlagged**: Has the email been flagged (starred, or pinned)?
-* **isDraft**: Is the email a draft?
-* **isAnswered**: Has the email been answered (replied to)?
-
-Messages are **immutable** other than these 4 flags and the set of mailboxes to which it belongs. To change anything else, the message must be deleted and a new one created (which will get a different id).
-
-Related messages are grouped into threads. When getting a message list you can collapse threads, which means the thread is only returned **once**, at the position of the first message in the thread in the non-collapsed list (given the sort order).
-
-### Contacts
-
-The contacts API is designed to be compatible with existing CardDAV systems. Each contact must have a unique, immutable id. A contact can be assigned to contact groups in a many-to-many relationship. A group is simply a named set of contacts. Contacts are completely mutable.
-
-### Calendars
-
-The calendars API is designed to be compatible with existing CalDAV systems. Each event must have a unique, immutable id. An event must belong to a single calendar. Events are completely mutable.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/mailbox.mdwn b/server/protocols/jmap-draft/doc/specs/spec/mailbox.mdwn
deleted file mode 100644
index 253a0a9..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/mailbox.mdwn
+++ /dev/null
@@ -1,342 +0,0 @@
-## Mailboxes
-
-A mailbox represents a named set of emails. This is the primary mechanism for organising messages within an account. It is analogous to a folder in IMAP or a label in other systems. A mailbox may perform a certain role in the system.
-
-A **Mailbox** object has the following properties:
-
-- **id**: `String`
-  The id of the mailbox. This property is immutable.
-- **name**: `String`
-  User-visible name for the mailbox, e.g. "Inbox". This may be any UTF-8 string of at least 1 character in length and maximum 256 bytes in size. Mailboxes MAY have the same name as a sibling Mailbox.
-- **parentId**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The mailbox id for the parent of this mailbox, or `null` if this mailbox is at the top level. Mailboxes form acyclic graphs (forests) directed by the child-to-parent relationship. There MUST NOT be a loop.
-- **role**: `String|null`
-  <aside class="notice">
-  The role of a mailbox depends actually on the mailbox name.
-  We should implement RFC 6154 in James in order to fully support this feature.
-  </aside>
-  Identifies system mailboxes. This property is immutable.
-
-  The following values SHOULD be used for the relevant mailboxes:
-
-  - `inbox` – the mailbox to which new mail is delivered by default, unless diverted by a rule or spam filter etc.
-  - `archive` – messages the user does not need right now, but does not wish to delete.
-  - `drafts` – messages the user is currently writing and are not yet sent.
-  - `outbox` – messages the user has finished writing and wishes to send (see the `setMessages` method description for more information). A mailbox with this role MUST be present if the user is allowed to send mail through an account. If not present, the user MAY NOT send mail with that account.
-  - `sent` – messages the user has sent.
-  - `trash` – messages the user has deleted.
-  - `spam` – messages considered spam by the server.
-  - `templates` – drafts which should be used as templates (i.e. used as the basis for creating new drafts).
-
-  No two mailboxes may have the same role. Mailboxes without a known purpose MUST have a role of `null`.
-
-  An account is not required to have mailboxes with any of the above roles. A client MAY create new mailboxes with a role property to help them keep track of a use-case not covered by the above list. To avoid potential conflict with any special behaviour a server might apply to mailboxes with certain roles in the future, any roles not in the above list created by the client must begin with `"x-"`. The client MAY attempt to create mailboxes with the standard roles if not already present, but the server MAY reject these.
-- **sortOrder**: `Number`
-  Defines the sort order of mailboxes when presented in the UI, so it is
-  consistent between devices. The number MUST be an integer in the range
-  0 <= sortOrder < 2^31.
-
-  A mailbox with a lower order should be displayed before a mailbox with
-  a higher order (but the same parent) in any mailbox listing in the
-  client's UI. Mailboxes with equal order should be sorted in
-  alphabetical order by name. The sorting should take into locale-specific
-  character order convention.
-- **mustBeOnlyMailbox**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If true, messages in this mailbox may not also be in any other mailbox.
-- **mayReadItems**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If true, may use this mailbox as part of a filter in a *getMessageList* call.
-  If a submailbox is shared but not the parent mailbox, this may be `false`.
-- **mayAddItems**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The user may add messages to this mailbox (by either creating a new message or modifying an existing one).
-- **mayRemoveItems**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The user may remove messages from this mailbox (by either changing the mailboxes of a message or deleting it).
-- **mayCreateChild**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The user may create a mailbox with this mailbox as its parent.
-- **mayRename**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The user may rename the mailbox or make it a child of another mailbox.
-- **mayDelete**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The user may delete the mailbox itself.
-- **totalMessages**: `Number`
-  The number of messages in this mailbox.
-- **unreadMessages**: `Number`
-  The number of messages in this mailbox where the *isUnread* property of the message is `true` and the *isDraft* property is `false`.
-- **totalThreads**: `Number`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The number of threads where at least one message in the thread is in this mailbox (but see below for special case handling of Trash).
-- **unreadThreads**: `Number`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The number of threads where at least one message in the thread has `isUnread == true` and `isDraft == false` AND at least one message in the thread is in this mailbox (but see below for special case handling of Trash). Note, the unread message does not need to be the one in this mailbox.
-
-The Trash mailbox (that is a mailbox with `role == "trash"`) MUST be treated specially:
-<aside class="warning">
-Not implemented
-</aside>
-
-* Messages in the Trash are ignored when calculating the `unreadThreads` and `totalThreads` count of other mailboxes.
-* Messages not in the Trash are ignored when calculating the `unreadThreads` and `totalThreads` count for the Trash folder.
-
-The result of this is that messages in the Trash are treated as though they are in a separate thread for the purposes of mailbox counts. It is expected that clients will hide messages in the Trash when viewing a thread in another mailbox and vice versa. This allows you to delete a single message to the Trash out of a thread.
-
-For example, suppose you have an account where the entire contents is a single conversation with 2 messages: an unread message in the Trash and a read message in the Inbox. The `unreadThreads` count would be `1` for the Trash and `0` for the Inbox.
-
-### getMailboxes
-
-Mailboxes can either be fetched explicitly by id, or all of them at once. To fetch mailboxes, make a call to `getMailboxes`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The Account to fetch the mailboxes for. If `null`, the primary account is used.
-- **ids**: `String[]|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The ids of the mailboxes to fetch. If `null`, all mailboxes in the account are returned.
-- **properties**: `String[]|null`
-  The properties of each mailbox to fetch. If `null`, all properties are returned. The id of the mailbox will **always** be returned, even if not explicitly requested.
-
-The response to *getMailboxes* is called *mailboxes*. It has the following arguments:
-
-- **accountId**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account used for the call.
-- **state**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  A string representing the state on the server for **all** mailboxes. If the state of a mailbox changes, or a new mailbox is created, or a mailbox is destroyed, this string will change. It is used to get delta updates.
-- **list**: `Mailbox[]`
-  An array of the Mailbox objects requested. This will be the **empty array** if the *ids* argument was the empty array, or contained only ids for mailboxes that could not be found.
-- **notFound**: `String[]|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  This array contains the ids passed to the method for mailboxes that do not exist, or `null` if all requested ids were found. It will always be `null` if the *ids* argument in the call was `null`.
-
-The following errors may be returned instead of the *mailboxes* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### getMailboxUpdates
-<aside class="warning">
-Not implemented
-</aside>
-
-The *getMailboxUpdates* call allows a client to efficiently update the state of its cached mailboxes to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *mailboxes* response. The server will return the changes made since this state.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *mailboxUpdates* response, an implicit call will be made to *getMailboxes* with the *changed* property of the response as the *ids* argument, and the *fetchRecordProperties* argument as the *properties* argument. If `false` or `null`, no implicit call will be made.
-- **fetchRecordProperties**: `String[]|null`
-  If `null`, all Mailbox properties will be fetched unless *onlyCountsChanged* in the *mailboxUpdates* response is `true`, in which case only the 4 counts properties will be returned (*totalMessages*, *unreadMessages*, *totalThreads* and *unreadThreads*). If not `null`, this value will be passed through to the *getMailboxes* call regardless of the *onlyCountsChanged* value in the *mailboxUpdates* response.
-
-The response to *getMailboxUpdates* is called *mailboxUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **changed**: `String[]`
-  An array of Mailbox ids where a property of the mailbox has changed between the old state and the new state, or the mailbox has been created, and the mailbox has not been destroyed.
-- **removed**: `String[]`
-  An array of Mailbox ids for mailboxes which have been destroyed since the old state.
-- **onlyCountsChanged**: `Boolean`
-  Indicates that only the folder counts (unread/total messages/threads) have changed since the old state. The client can then use this to optimise its data transfer and only fetch the counts. If the server is unable to tell if only counts have changed, it should just always return `false`.
-
-If a mailbox has been modified AND deleted since the oldState, the server should just return the id in the *removed* array, but MAY return it in the *changed* array as well. If a mailbox has been created AND deleted since the oldState, the server SHOULD remove the mailbox id from the response entirely, but MAY include it in the *removed* array, and optionally the *changed* array as well.
-
-The following errors may be returned instead of the `mailboxUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old. The client MUST invalidate its Mailbox cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setMailboxes
-<aside class="notice">
-The mailbox name property should not contain the special '.' (dot) character. 
-</aside>
-
-Mailboxes can be created, updated and destroyed using the *setMailboxes* method. The method takes the following arguments:
-
-- **accountId**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account to use for this call. If `null`, defaults to the primary account.
-- **ifInState**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  This is a state string as returned by the *getMailboxes* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned.
-- **create**: `String[Mailbox]|null`
-  A map of *creation id* (an arbitrary string set by the client) to Mailbox objects. If `null`, no objects will be created.
-- **update**: `String[Mailbox]|null`
-  A map of id to a an object containing the properties to update for that Mailbox. If `null`, no objects will be updated.
-- **destroy**: `String[]|null`
-  A list of ids for Mailboxes to permanently delete. If `null`, no objects will be deleted.
-
-Each create, update or destroy is considered an atomic unit. The server MAY commit some of the changes but not others, however MAY NOT only commit part of an update to a single record (e.g. update the *name* field but not the *parentId* field, if both are supplied in the update object).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-####  Creating mailboxes
-<aside class="notice">
-Only Name, Role & ParentId properties are supported.
-</aside>
-
-The properties of the Mailbox object submitted for creation MUST conform to the following conditions:
-
-- The *id* property MUST NOT be present.
-- The *parentId* property MUST be either `null` or be a valid id for a mailbox for which the `mayCreateChild` property is `true`.
-- The *role* property MUST be either `null`, a valid role as listed in the Mailbox object specification, or prefixed by `"x-"`.
-- The *mustBeOnlyMailbox* property MUST NOT be present. This is server dependent and will be set by the server.
-- The *mayXXX* properties MUST NOT be present. Restrictions may only be set by the server for system mailboxes, or when sharing mailboxes with other users (setting sharing is not defined yet in this spec).
-- The *totalMessages*, *unreadMessages*, *totalThreads* and *unreadThreads* properties MUST NOT be present.
-
-If any of the properties are invalid, the server MUST reject the create with an `invalidProperties` error. The Error object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-There may be a maximum number of mailboxes allowed on the server. If this is reached, any attempt at creation will be rejected with a `maxQuotaReached` error.
-
-####  Updating mailboxes
-<aside class="notice">
-Due to cycle detection, mailboxes with child cannot be updated. 
-</aside>
-
-If the *id* given does not correspond to a Mailbox in the given account, the update MUST be rejected with a `notFound` error.
-
-All properties being updated must be of the correct type, not immutable or server-set-only, and the new value must obey all conditions of the property. In particular, note the following conditions:
-
-- The *name* property MUST be valid UTF-8, between 1 and 256 bytes in size.
-- The *parentId* property MUST be either `null` or be a valid id for *another* mailbox that is **not a descendant** of this mailbox, and for which the `mayCreateChild` property is `true`.
-  <aside class="notice">
-  Only Name, Role & ParentId properties are supported.
-  </aside>
-- These properties are immutable or may only be set by the server:
-  - id
-  - role
-  - mustBeOnlyMailbox
-  - mayReadMessageList
-  - mayAddMessages
-  - mayRemoveMessages
-  - mayCreateChild
-  - mayRenameMailbox
-  - mayDeleteMailbox
-  - totalMessages
-  - unreadMessages
-  - totalThreads
-  - unreadThreads
-
-If any of the properties are invalid, the server MUST reject the update with an `invalidProperties` error. The Error object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-#### Destroying mailboxes
-
-If the *id* given does not correspond to a Mailbox in the given account, the destruction MUST be rejected with a `notFound` error.
-
-If the mailbox has `mayDeleteMailbox == false`, the destruction MUST be rejected with a `forbidden` error.
-
-A mailbox MAY NOT be destroyed if it still has any child mailboxes. Attempting to do so will result in the destruction being rejected with a `mailboxHasChild` error.
-
-Destroying a mailbox MUST NOT delete any messages still contained within it. It only removes them from the mailbox. Since messages MUST always be in at least one mailbox, if the last mailbox they are in is deleted the messages must be added to the mailbox with `role == "inbox"`. If no Inbox exists, the messages must be moved to any other mailbox; this is server dependent.
-
-There MUST always be **at least one** mailbox. It is expected that the server will enforce this by setting `mayDeleteMailbox == false` on at least the Inbox, if not all system mailboxes. However, if this is not the case, an attempt to destroy the last mailbox MUST still be rejected with a `mailboxRequired` error.
-
-#### Ordering of changes
-
-Each individual create, update or destroy MUST take the server from one valid state to another valid state, so the changes can be processed linearly without need to undo or backtrack. However the order of the changes may affect the validity of the change. The server MUST process the changes in a manner which is indistinguishable from an order following these rules:
-
-1. Creates comes before any other changes.
-2. Creates with a parentId that is not a *creation id* reference come before those that reference another newly created mailbox.
-3. Creation of a mailbox X comes before creation of a mailbox Y if Y will be a descendent of X.
-4. Updates come before destroying mailboxes.
-5. Update/Destroy X comes before update/destroy Y if X is a descendent of Y.
-
-#### Response
-
-The response to *setMailboxes* is called *mailboxesSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by `getMailboxes` before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by `getMailboxes`.
-- **created**: `String[Mailbox]`
-  A map of the creation id to an object containing the **id** and **mustBeOnlyMailbox** properties for each successfully created Mailbox.
-- **updated**: `String[]`
-  A list of ids for Mailboxes that were successfully updated.
-- **destroyed**: `String[]`
-  A list of ids for Mailboxes that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each Mailbox that failed to be created. The possible errors are defined above.
-- **notUpdated**: `String[SetError]`
-  A map of Mailbox id to a SetError object for each Mailbox that failed to be updated. The possible errors are defined above.
-- **notDestroyed**: `String[SetError]`
-  A map of Mailbox id to a SetError object for each Mailbox that failed to be destroyed. The possible errors are defined above.
-
-The following errors may be returned instead of the *mailboxesSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an `ifInState` argument was supplied and it does not match the current state.
-
-Example request:
-
-    [ "setMailboxes", {
-      "ifInState": "ms4123",
-      "update": {
-        "f3": {
-          "name": "The new name"
-        }
-      },
-      "destroy": [ "f5" ]
-    }, "#0" ]
diff --git a/server/protocols/jmap-draft/doc/specs/spec/message.mdwn b/server/protocols/jmap-draft/doc/specs/spec/message.mdwn
deleted file mode 100644
index debcd8a..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/message.mdwn
+++ /dev/null
@@ -1,533 +0,0 @@
-## Messages
-
-Just like in IMAP, a message is **immutable** except for the boolean `isXXX` status properties and the set of mailboxes it is in. This allows for more efficient caching of messages, and gives easier backwards compatibility for servers implementing an IMAP interface to the same data.
-
-JMAP completely hides the complexities of MIME. All special encodings of either headers or the body, such as [base64](https://tools.ietf.org/html/rfc4648), or [RFC2047](http://tools.ietf.org/html/rfc2047) encoding of non-ASCII characters, MUST be fully decoded into standard UTF-8.
-
-A **Message** object has the following properties:
-
-- **id**: `String`
-  <aside class="notice">
-  Work in progress. Unique message id is generated by concatenate the username, the mailboxId and the uid.
-  </aside>
-  The id of the message.
-- **blobId**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id representing the raw RFC2822 message. This may be used to download
-  the original message or to attach it directly to another message etc.
-- **threadId**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the thread to which this message belongs.
-- **mailboxIds**: `String[]` (Mutable)
-  The ids of the mailboxes the message is in. A message MUST belong to one or more mailboxes at all times (until it is deleted).
-- **inReplyToMessageId**: `String|null`
-  The id of the Message this message is a reply to. This is primarily for drafts, but the server MAY support this for received messages as well by looking up the RFC2822 Message-Id referenced in the `In-Reply-To` header and searching for this message in the user's mail.
-- **isUnread**: `Boolean` (Mutable)
-  Has the message not yet been read? This corresponds to the **opposite** of the `\Seen` system flag in IMAP.
-- **isFlagged**: `Boolean` (Mutable)
-  Is the message flagged? This corresponds to the `\Flagged` system flag in IMAP.
-- **isAnswered**: `Boolean` (Mutable)
-  Has the message been replied to? This corresponds to the `\Answered` system flag in IMAP.
-- **isDraft**: `Boolean` (Mutable by the server only)
-  Is the message a draft? This corresponds to the `\Draft` system flag in IMAP.
-- **hasAttachment**: `Boolean`
-  Does the message have any attachments?
-- **headers**: `String[String]`
-  A map of header name to (decoded) header value for all headers in the message. For headers that occur multiple times (e.g. `Received`), the values are concatenated with a single new line (`\n`) character in between each one.
-- **from**: `Emailer|null`
-  An Emailer object (see below) containing the name/email from the parsed `From` header of the email. If the email doesn't have a `From` header, this is `null`.
-- **to**:  `Emailer[]|null`
-  An array of name/email objects (see below) representing the parsed `To` header of the email, in the same order as they appear in the header. If the email doesn't have a `To` header, this is `null`. If the header exists but does not have any content, the response is an array of zero length.
-- **cc**:  `Emailer[]|null`
-  An array of name/email objects (see below) representing the parsed `Cc` header of the email, in the same order as they appear in the header. If the email doesn't have a `Cc` header, this is `null`. If the header exists but does not have any content, the response is an array of zero length.
-- **bcc**:  `Emailer[]|null`
-  An array of name/email objects (see below) representing the parsed `Bcc` header of the email. If the email doesn't have a `Bcc` header (which will be true for most emails outside of the Sent mailbox), this is `null`. If the header exists but does not have any content, the response is an array of zero length.
-- **replyTo**: `Emailer|null`
-  An Emailer object (see below) containing the name/email from the parsed `Reply-To` header of the email. If the email doesn't have a `Reply-To` header, this is `null`.
-- **subject**: `String`
-  The subject of the message.
-- **date**: `Date`
-  The date the message was sent (or saved, if the message is a draft).
-- **size**: `Number`
-  The size in bytes of the whole message as counted by the server towards the user's quota.
-- **preview**: `String`
-  Up to 256 characters of the beginning of a plain text version of the message body. This is intended to be shown as a preview line on a mailbox listing, and the server may choose to skip quoted sections or salutations to return a more useful preview.
-- **textBody**: `String|null`
-  The plain text body part for the message. If there is only an HTML version of the body, a plain text version will be generated from this.
-- **htmlBody**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The HTML body part for the message if present. If there is only a plain text version of the body, an HTML version will be generated from this. Any scripting content, or references to external plugins, MUST be stripped from the HTML by the server.
-- **attachments**: `Attachment[]|null`
-  An array of attachment objects (see below) detailing all the attachments to the message.
-- **attachedMessages**: `String[Message]|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  An object mapping attachment id (as found in the `attachments` property) to a **Message** object with the following properties, for each RFC2822 message attached to this one:
-  - headers
-  - from
-  - to
-  - cc
-  - bcc
-  - replyTo
-  - subject
-  - date
-  - textBody
-  - htmlBody
-  - attachments
-  - attachedMessages
-
-An **Emailer** object has the following properties:
-
-- **name**: `String`
-  The name of the sender/recipient. If a name cannot be extracted for an email, this property should be the empty string.
-- **email**: `String`
-  The email address of the sender/recipient. This MUST be of the form `"<mailbox>@<host>"` If a `host` or even `mailbox` cannot be extracted for an email, the empty string should be used for this part (so the result will always still contain an `"@"`).
-
-Example Emailer object:
-
-    [
-        {name:"Joe Bloggs", email:"joeb@example.com"},
-        {name:"", email:"john@example.com"},
-        {name:"John Smith", email: "john@"}
-    ]
-
-An **Attachment** object has the following properties:
-
-- **blobId**: `String`
-  The id of the binary data.
-- **type**: `String`
-  The content-type of the attachment.
-- **name**: `String`
-  The full file name, e.g. "myworddocument.doc"
-- **size**: `Number`
-  The size, in bytes, of the attachment when fully decoded (i.e. the number of bytes in the file the user would download).
-- **cid**: `String|null`
-  The id used within the message body to reference this attachment. This is only unique when paired with the message id, and has no meaning without reference to that.
-- **isInline**: `Boolean`
-  True if the attachment is referenced by a `cid:` link from within the HTML body of the message.
-- **width**: `Number|null`
-  The width (in px) of the image, if the attachment is an image.
-- **height**: `Number|null`
-  The height (in px) of the image, if the attachment is an image.
-
-### getMessages
-
-Messages can only be fetched explicitly by id. To fetch messages, make a call to `getMessages`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **ids**: `String[]`
-  An array of ids for the messages to fetch.
-- **properties**: `String[]|null`
-  A list of properties to fetch for each message. If `null`, all properties will be fetched.
-
-The `id` property is always returned, regardless of whether it is in the list of requested properties. The possible values for `properties` can be found above in the description of the Message object. In addition to this, the client may request the following special values:
-
-- **body**: If `"body"` is included in the list of requested properties, it will be interpreted by the server as a request for `"htmlBody"` if the message has an HTML part, or `"textBody"` otherwise.
-- **headers.property**: Instead of requesting all the headers (by requesting the `"headers"` property, the client may specify the particular headers it wants using the `headers.property-name` syntax, e.g. `"headers.X-Spam-Score", "headers.X-Spam-Hits"`). The server will return a *headers* property but with just the requested headers in the object rather than all headers. If `"headers"` is requested, the server MUST ignore the individual header requests and just return all headers. If a requested header is not present in the message, it MUST not be present in the *headers* object. Header names are case-insensitive.
-
-The response to *getMessages* is called *messages*. It has the following arguments:
-
-- **accountId**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account used for the call.
-- **state**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  A string encoding the current state on the server. This string will change
-  if any messages change (that is, a new message arrives, a change is made to one of the mutable properties, or a message is deleted). It can be passed to *getMessageUpdates* to efficiently get the list of changes from the previous state.
-- **list**: `Message[]`
-  An array of Message objects for the requested message ids. This may not be in the same order as the ids were in the request.
-- **notFound**: `String[]|null`
-  An array of message ids requested which could not be found, or `null` if all
-  ids were found.
-
-The following errors may be returned instead of the *messages* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-Example request:
-
-    ["getMessages", {
-      "ids": [ "f123u456", "f123u457" ],
-      "properties": [ "threadId", "mailboxIds", "from", "subject", "date" ]
-    }, "#1"]
-
-and response:
-
-    ["messages", {
-      "state": "41234123231",
-      "list": [
-        {
-          messageId: "f123u457",
-          threadId: "ef1314a",
-          mailboxIds: [ "f123" ],
-          from: [{name: "Joe Bloggs", email: "joe@bloggs.com"}],
-          subject: "Dinner on Thursday?",
-          date: "2013-10-13T14:12:00Z"
-        }
-      ],
-      notFound: [ "f123u456" ]
-    }, "#1"]
-
-
-### getMessageUpdates
-<aside class="warning">
-Not implemented
-</aside>
-
-If a call to *getMessages* returns with a different *state* string in the response to a previous call, the state of the messages has changed on the server. For example, a new message may have been delivered, or an existing message may have changed mailboxes.
-
-The *getMessageUpdates* call allows a client to efficiently update the state of any cached messages to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *messages* response. The server will return the changes made since this state.
-- **maxChanges**: `Number|null`
-  The maximum number of changed messages to return in the response. The server MAY choose to clamp this value to a particular maximum or set a maximum if none is given by the client. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
-- **fetchRecords**: `Boolean|null`
-  If true, after outputting a *messageUpdates* response, an implicit call will be made to *getMessages* with a list of all message ids in the *changed* argument of the response as the *ids* argument, and the *fetchRecordProperties* argument as the *properties* argument.
-- **fetchRecordProperties**: `String[]|null`
-  The list of properties to fetch on any fetched messages. See *getMessages* for a full description.
-
-The response to *getMessageUpdates* is called *messageUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **hasMoreUpdates**: `Boolean`
-  If `true`, the client may call *getMessageUpdates* again with the *newState* returned to get further updates. If `false`, *newState* is the current server state.
-- **changed**: `String[]`
-  An array of message ids for messages that have either been created or had their state change, and are not currently deleted.
-- **removed**: `String[]`
-  An array of message ids for messages that have been deleted since the oldState.
-
-If a *maxChanges* is supplied, or set automatically by the server, the server must try to limit the number of ids across *changed* and *removed* to the number given. If there are more changes than this between the client's state and the current server state, the update returned MUST take the client to an intermediate state, from which the client can continue to call *getMessageUpdates* until it is fully up to date. The server MAY return more ids than the *maxChanges* total if this is required for it to be able to produce an update to an intermediate state, but it SHOULD try to keep it close to the maximum requested.
-
-If a message has been modified AND deleted since the oldState, the server should just return the id in the *removed* response, but MAY return it in the changed response as well. If a message has been created AND deleted since the oldState, the server should remove the message id from the response entirely, but MAY include it in the *removed* response, and (if in the *removed* response) MAY included it in the *changed* response as well.
-
-The following errors may be returned instead of the *messageUpdates* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old, or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its Message cache. The error object MUST also include a `newState: String` property with the current state for the type.
-
-### setMessages
-
-The *setMessages* method encompasses:
-
-- Creating a draft message
-<aside class="warning">Not implemented</aside>
-- Sending a message
-- Changing the flags of a message (unread/flagged status)
-
-- Adding/removing a message to/from mailboxes (moving a message)
-- Deleting messages
-
-It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **ifInState**: `String|null`
-  This is a state string as returned by the *getMessages* method. If supplied, the string must match the current state, otherwise the method will be aborted and a `stateMismatch` error returned.
-- **create**: `String[Message]|null`
-  A map of *creation id* (an arbitrary string set by the client) to Message objects (see below for a detailed description).
-- **update**: `String[Message]|null`
-  A map of id to a an object containing the properties to update for that Message.
-- **destroy**: `String[]|null`
-  A list of ids for Message objects to permanently delete.
-
-Each create, update or destroy is considered an atomic unit. It is permissible for the server to commit some of the changes but not others, however it is not permissible to only commit part of an update to a single record (e.g. update the *isFlagged* field but not the *mailboxIds* field, if both are supplied in the update object for a message).
-
-If a create, update or destroy is rejected, the appropriate error should be added to the notCreated/notUpdated/notDestroyed property of the response and the server MUST continue to the next create/update/destroy. It does not terminate the method.
-
-If an id given cannot be found, the update or destroy MUST be rejected with a `notFound` set error.
-
-#### Saving a draft
-
-Creating messages via the *setMessages* method is only for creating draft messages and sending them. For delivering/importing a complete RFC2822 message, use the `importMessages` method.
-
-The properties of the Message object submitted for creation MUST conform to the following conditions:
-
-- **id**: This property MUST NOT be included. It is set by the server upon creation.
-- **blobId**: This property MUST NOT be included. It is set by the server upon creation.
-- **threadId**: This property MUST NOT be included. It is set by the server upon creation.
-- **mailboxIds**: This property MUST be included. The value MUST include the id of either the mailbox with `role == "drafts"` (to save a draft) or the mailbox with `role == "outbox"` (to send the message). If this mailbox does not have `mustBeOnlyMailbox == true`, others may be included too.
-- **inReplyToMessageId**: Optional. If included, the server will look up this message and if found set appropriate `References` and `In-Reply-To` headers. These will override any such headers supplied in the *headers* property. If not found, the creation MUST be rejected with an `inReplyToNotFound` error.
-- **isUnread**: Optional, defaults to `false`. If included this MUST be `false`.
-- **isFlagged**: Optional, defaults to `false`.
-- **isAnswered**: Optional, defaults to `false`. If included this MUST be `false`.
-- **isDraft**: Optional, defaults to `true`. If included this MUST be `true`.
-- **hasAttachment**: This property MUST NOT be included. It is set by the server upon creation based on the attachments property.
-- **headers**: Optional. The keys MUST only contain the characters A-Z, a-z, 0-9 and hyphens.
-- **from**: Optional. Overrides a "From" in the *headers*.
-- **to**: Optional. Overrides a "To" in the *headers*.
-- **cc**: Optional. Overrides a "Cc" in the *headers*.
-- **bcc**:  Optional. Overrides a "Bcc" in the *headers*.
-- **replyTo**: Optional. Overrides a "Reply-To" in the *headers*.
-- **subject**: Optional. Defaults to the empty string (`""`).
-- **date**: Optional. If included, the server SHOULD wait until this time to send the message (once moved to the outbox folder). Until it is sent, the send may be cancelled by moving the message back out of the outbox folder. If the date is in the past, the message must be sent immediately. A client may find out if the server supports delayed sending by querying the capabilities property of the Account object.
-- **size**: This MUST NOT be included. It is set by the server upon creation.
-- **preview**: This MUST NOT be included. It is set by the server upon creation.
-- **textBody**: Optional. If not supplied and an htmlBody is, the server SHOULD generate a text version for the message from this.
-- **htmlBody**: Optional. If this contains internal links (cid:) the cid value should be the attachment id.
-- **attachments**: Optional. An array of Attachment objects detailing all the attachments to the message. To add an attachment, the file must first be uploaded using the standard upload mechanism; this will give the client a URL that may be used to identify the file. The `id` property may be assigned by the client, and is solely used for matching up with `cid:<id>` links inside the `htmlBody`. The server MAY (and probably will) change the ids upon sending.
-
-  If one of the attachments is not found, the creation MUST be rejected with an `invalidProperties` error. An extra property SHOULD be included in the error object called `attachmentsNotFound`, of type `String[]`, which should be an array of the ids of any attachments that could not be found on the server.
-- **attachedMessages**: This MUST NOT be included.
-
-All optional properties default to `null` unless otherwise stated. Where included, properties MUST conform to the type given in the Message object definition.
-
-If any of the properties are invalid, the server MUST reject the create with an `invalidProperties` error. The Error object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-Other than making sure it conforms to the correct type, the server MUST NOT attempt to validate from/to/cc/bcc when saved as a draft. This is to ensure messages can be saved at any point. Validation occurs when the user tries to send a message.
-
-If a draft cannot be saved due to the user reaching their maximum mail storage quota, the creation MUST be rejected with a `maxQuotaReached` error.
-
-#### Updating messages
-
-Messages are mainly immutable, so to update a draft the client must create a new message and delete the old one. This ensures that if the draft is also being edited elsewhere, the two will split into two different drafts to avoid data loss.
-
-Only the following properties may be modified:
-
-- **mailboxIds**: The server MUST reject any attempt to add a message with `isDraft == false` to the outbox. The server MAY reject attempts to add a draft message to a mailbox that does not have a role of `drafts`, `outbox` or `templates`.
-- **isFlagged**
-- **isUnread**
-- **isAnswered**
-
-Note, a mailbox id may be a *creation id* (see `setFoos` for a description of how this works).
-
-If any of the properties in the update are invalid (immutable and different to the current server value, wrong type, invalid value for the property – like a mailbox id for non-existent mailbox), the server MUST reject the update with an `invalidProperties` error. The Error object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-If the *id* given does not correspond to a Message in the given account, reject the update with a `notFound` error.
-
-To **delete a message** to trash, simply change the `mailboxIds` property so it is now in the mailbox with `role == "trash"`. If the mailbox has the property `mustBeOnlyMailbox == true`, it must be removed from all other mailboxes. Otherwise, leave it in those mailboxes so that it will be restored to its previous state if undeleted.
-<aside class="warning">Not implemented</aside>
-
-#### Sending messages
-
-To send a message, either create a new message directly into the mailbox with `role == "outbox"` or move an existing draft into this mailbox. At this point the server will check that it has everything it needs for a valid message. In particular, that it has a valid "From" address, it has at least one address to send to, and all addresses in To/Cc/Bcc are valid email addresses. If it cannot send, it will reject the creation/update with an `invalidProperties` error. The Error object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object SHOULD also contain a *description* property of type `String` with a user-friendly description of the problems to present to the user.
-
-If the message is accepted, the server should **asynchronously** schedule the message to be sent **after** this method call is complete (note, this MAY occur before the next method in the same API request or after the whole API request is complete). This means that the `newState` string in the response represents a state where the message is still in the outbox. When the message is sent, the server MUST delete the message from the **outbox** and SHOULD create a **new** copy of the sent message (with a new id) in the **sent** mailbox, unless the user has indicated another preference. If `inReplyToMessageId` was set, the server SHOULD mark this message as `isAnswered: true` at this point, if found.
-
-#### Cancelling a send
-
-A message may be moved out of the **outbox** and back to the **drafts** mailbox using the standard update message mechanism, if it has not yet been sent at the time the method is called. This MUST cancel the queued send. If the message has already been sent then it will have been deleted from the outbox, so the update will fail with a standard `notFound` error.
-
-#### Destroying messages
-
-If the *id* given does not correspond to a Message in the given account, the server MUST reject the destruction with a `notFound` error.
-
-Destroying a message removes it from all mailboxes to which it belonged.
-
-#### Response
-
-The response to *setMessages* is called *messagesSet*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String|null`
-  The state string that would have been returned by *getMessages* before making the requested changes, or `null` if the server doesn't know what the previous state string was.
-- **newState**: `String`
-  The state string that will now be returned by *getMessages*.
-- **created**: `String[Message]`
-  A map of the creation id to an object containing the *id*, *blobId*, *threadId*, and *size* properties for each successfully created Message.
-- **updated**: `String[]`
-  A list of Message ids for Messages that were successfully updated.
-- **destroyed**: `String[]`
-  A list of Message ids for Messages that were successfully destroyed.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each Message that failed to be created. The possible errors are defined above.
-- **notUpdated**: `String[SetError]`
-  A map of Message id to a SetError object for each Message that failed to be updated. The possible errors are defined above.
-- **notDestroyed**: `String[SetError]`
-  A map of Message id to a SetError object for each Message that failed to be destroyed. The possible errors are defined above.
-
-The following errors may be returned instead of the *messagesSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`stateMismatch`: Returned if an *ifInState* argument was supplied and it does not match the current state.
-
-### importMessages
-<aside class="warning">
-Not implemented
-</aside>
-
-The *importMessages* method adds RFC2822 messages to a user's set of messages. The messages must first be uploaded as a file using the standard upload mechanism. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, defaults to the primary account.
-- **messages**: `String[MessageImport]`
-  A map of creation id (client specified) to MessageImport objects
-
-An **ImportMessage** object has the following properties:
-
-- **file**: `String`
-  The URL of the uploaded file (see the file upload section).
-- **mailboxIds** `String[]`
-  The ids of the mailbox(es) to assign this message to.
-- **isUnread**: `Boolean`
-- **isFlagged**: `Boolean`
-- **isAnswered**: `Boolean`
-- **isDraft**: `Boolean`
-
-If `isDraft == true`, the mailboxes MUST include the drafts or outbox mailbox. Adding to the outbox will send the message, as described in the *setMessages* section (it will NOT automatically mark any other message as *isAnswered*).
-
-The response to *importMessages* is called *messagesImported*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for this call.
-- **created**: `String[Message]`
-  A map of the creation id to an object containing the *id*, *blobId*, *threadId* and *size* properties for each successfully imported Message.
-- **notCreated**: `String[SetError]`
-  A map of creation id to a SetError object for each Message that failed to be created. The possible errors are defined above.
-
-The following errors may be returned instead of the *messageImported* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`notFound`: Returned if the URL given in the `file` argument does not correspond to an internal file.
-
-`invalidMailboxes`: Returned if one of the mailbox ids cannot be found, or an invalid combination of mailbox ids is specified.
-
-`maxQuotaReached`: Returned if the user has reached their mail quota so the message cannot be imported.
-
-### copyMessages
-<aside class="warning">
-Not implemented
-</aside>
-
-The only way to move messages **between** two different accounts is to copy them using the *copyMessages* method, then once the copy has succeeded, delete the original. It takes the following arguments:
-
-- **fromAccountId**: `String|null`
-  The id of the account to copy messages from. If `null`, defaults to the primary account.
-- **toAccountId**: `String|null`
-  The id of the account to copy messages to. If `null`, defaults to the primary account.
-- **messages**: `String[MessageCopy]`
-  A map of *creation id* to a MessageCopy object.
-
-A **MessageCopy** object has the following properties:
-
-- **messageId**: `String`
-  The id of the message to be copied in the "from" account.
-- **mailboxIds**: `String[]`
-  The ids of the mailboxes (in the "to" account) to add the copied message to.
-- **isUnread**: `Boolean`
-  The *isUnread* property for the copy.
-- **isFlagged**: `Boolean`
-  The *isFlagged* property for the copy.
-- **isAnswered**: `Boolean`
-  The *isAnswered* property for the copy.
-- **isDraft**: `Boolean`
-  The *isDraft* property for the copy.
-
-The "from" account may be the same as the "to" account to copy messages within an account.
-
-The response to *copyMessages* is called *messagesCopied*. It has the following arguments:
-
-- **fromAccountId**: `String`
-  The id of the account messages were copied from.
-- **toAccountId**: `String`
-  The id of the account messages were copied to.
-- **created**: `String[Message]|null`
-  A map of the creation id to an object containing the *id*, *blobId*, *threadId* and *size* properties for each successfully copied Message.
-- **notCreated**: `String[SetError]|null`
-  A map of creation id to a SetError object for each Message that failed to be copied, `null` if none.
-
-The **SetError** may be one of the following types:
-
-`notFound`: Returned if the messageId given can't be found.
-
-`invalidMailboxes`: Returned if one of the mailbox ids cannot be found, or an invalid combination of mailbox ids is specified.
-
-`maxQuotaReached`: Returned if the user has reached their mail quota so the message cannot be copied.
-
-The following errors may be returned instead of the *messagesCopied* response:
-
-`fromAccountNotFound`: Returned if a *fromAccountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`toAccountNotFound`: Returned if a *toAccountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`fromAccountNoMail`: Returned if the *fromAccountId* given corresponds to a valid account, but does not contain any mail data.
-
-`toAccountNoMail`: Returned if the *toAccountId* given corresponds to a valid account, but does not contain any mail data.
-
-`accountReadOnly`: Returned if the "to" account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-### reportMessages
-<aside class="warning">
-Not implemented
-</aside>
-
-Messages can be reported as spam or non-spam to help train the user's spam filter. This MUST NOT affect the state of the Message objects (it DOES NOT move a message into or out of the Spam mailbox).
-
-To report messages, make a call to *reportMessages*. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **messageIds**: `String[]`
-  The list of ids of messages to report.
-- **asSpam**: `Boolean`
-  If `true`, learn these messages as spam. If `false`, learn as non-spam.
-
-
-The response to *reportMessages* is called *messagesReported*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for this call.
-- **asSpam**: `Boolean`
-  Echoed back from the call
-- **reported**: `String[]`
-  The ids of each message successfully reported.
-- **notFound**: `String`
-  The ids of each message not found.
-
-The following errors may be returned instead of the *messagesReported* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`accountReadOnly`: Returned if the account has `isReadOnly == true`.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/messagelist.mdwn b/server/protocols/jmap-draft/doc/specs/spec/messagelist.mdwn
deleted file mode 100644
index dc224e9..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/messagelist.mdwn
+++ /dev/null
@@ -1,341 +0,0 @@
-## MessageLists
-
-A **MessageList** is a sorted query on the set of messages in a user's account. Since it can be very long, the client must specify what section of the list to return. The client can optionally also fetch the threads and/or messages for this part of the list.
-
-The same message may appear in multiple messages lists. For example, it may belong to multiple mailboxes, and of course it can appear in searches. Since messages have an immutable id, a client can easily tell if it already has a message cached and only fetch the ones it needs.
-
-When the state changes on the server, a delta update can be requested to efficiently update the client's cache of this list to the new state. If the server doesn't support this, the client still only needs to fetch the message list again, not the messages themselves.
-
-### getMessageList
-
-To fetch a message list, make a call to *getMessageList*. It takes the following arguments:
-
-- **accountId**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **filter**: `FilterCondition|FilterOperator|null`
-  <aside class="notice">
-  Only filtering on a list of mailboxIds is supported
-  </aside>
-  Determines the set of messages returned in the results. See the "Filtering" section below for allowed values and semantics.
-- **sort**: `String[]|null`
-  A list of Message property names to sort by. See the "Sorting" section below for allowed values and semantics.
-- **collapseThreads**: `Boolean|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If true, each thread will only be returned once in the resulting list, at the position of the first message in the list (given the filter and sort order) belonging to the thread. If `false` or `null`, threads may be returned multiple times.
-- **position**: `Number|null`
-  The 0-based index of the first result in the list to return. If a negative value is given, the call MUST be rejected with an `invalidArguments` error. If `null`, 0 is used.
-- **anchor**: `String|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  A Message id. The index of this message id will be used in combination with the `anchorOffset` argument to determine the index of the first result to return (see the "Windowing" section below for more details).
-- **anchorOffset**: `Number|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The index of the anchor message relative to the index of the first result to return. This MAY be negative. For example, `-1` means the first message after the anchor message should be the first result in the results returned (see the "Windowing" section below for more details).
-- **limit**: `Number|null`
-  The maximum number of results to return. If `null`, no limit is presumed. The server MAY choose to enforce a maximum `limit` argument. In this case, if a greater value is given, the limit should be clamped to the maximum; since the total number of results in the list is returned, the client should not be relying on how many results are returned to determine if it has reached the end of the list. If a negative value is given, the call MUST be rejected with an `invalidArguments` error.
-- **fetchThreads**: `Boolean|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If `true`, after outputting a *messageList* response, an implicit call will be made to *getThreads* with the *threadIds* array in the response as the *ids* argument, and the *fetchMessages* and *fetchMessageProperties* arguments passed straight through from the call to *getMessageList*. If `false` or `null`, no implicit call will be made.
-- **fetchMessages**: `Boolean|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If `true` and `fetchThreads == false`, then after outputting a *messageList* response, an implicit call will be made to *getMessages* with the `messageIds` array in the response as the *ids* argument, and the *fetchMessageProperties* argument as the *properties* argument. If `false` or `null`, no implicit call will be made.
-- **fetchMessageProperties**: `String[]|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The list of properties to fetch on any fetched messages. See *getMessages* for a full description.
-- **fetchSearchSnippets**: `Boolean|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  If `true`, then after outputting a *messageList* and making any other implicit calls, an implicit call will be made to *getSearchSnippets*. The *messageIds* array from the response will be used as the *messageIds* argument, and the *filter* argument will be passed straight through. If `false` or `null`, no implicit call will be made.
-
-#### Filtering
-<aside class="notice">
-Only filtering on a list of mailboxIds is supported
-</aside>
-
-A **FilterOperator** object has the following properties:
-
-- **operator**: `String`
-  This MUST be one of the following strings: "AND"/"OR"/"NOT":
-  - **AND**: all of the conditions must match for the filter to match.
-  - **OR**: at least one of the conditions must match for the filter to match.
-  - **NOT**: none of the conditions must match for the filter to match.
-- **conditions**: `(FilterCondition|FilterOperator)[]`
-  The conditions to evaluate against each message.
-
-A **FilterCondition** object has the following properties:
-
-- **inMailboxes**: `String[]|null`
-  A list of mailbox ids. A message must be in ALL of these mailboxes to match the condition.
-- **notInMailboxes**: `String[]|null`
-  A list of mailbox ids. A message must NOT be in ANY of these mailboxes to match the condition.
-- **before**: `Date|null`
-  The date of the message (as returned on the Message object) must be before this date to match the condition.
-- **after**: `Date|null`
-  The date of the message (as returned on the Message object) must be on or after this date to match the condition.
-- **minSize**: `Number|null`
-  The size of the message in bytes (as returned on the Message object) must be equal to or greater than this number to match the condition.
-- **maxSize**: `Number|null`
-  The size of the message in bytes (as returned on the Message object) must be less than this number to match the condition.
-- **threadIsFlagged**: `Boolean|null`
-  If `true`, the condition is matched if the `isFlagged` property of *any* message in the same thread as the message being examined is `true`. If `false`, the `isFlagged` property of *every* message in the same thread as the message being examined must be `false` to match the condition.
-- **threadIsUnread**: `Boolean|null`
-  If `true`, the condition is matched if the `isUnread` property of *any* message in the same thread as the message being examined is `true`. If `false`, the `isUnread` property of *every* message in the same thread as the message being examined must be `false` to match the condition.
-- **isFlagged**: `Boolean|null`
-  The `isFlagged` property of the message must be identical to the value given to match the condition.
-- **isUnread**: `Boolean|null`
-  The `isUnread` property of the message must be identical to the value given to match the condition.
-- **isAnswered**: `Boolean|null`
-  The `isAnswered` property of the message must be identical to the value given to match the condition.
-- **isDraft**: `Boolean|null`
-  The `isDraft` property of the message must be identical to the value given to match the condition.
-- **hasAttachment**: `Boolean|null`
-  The `hasAttachment` property of the message must be identical to the value given to match the condition.
-- **text**: `String|null`
-  Looks for the text in the *from*, *to*, *cc*, *bcc*, *subject*, *textBody* or *htmlBody* properties of the message.
-- **from**: `String|null`
-  Looks for the text in the *from* property of the message.
-- **to**: `String|null`
-  Looks for the text in the *to* property of the message.
-- **cc**: `String|null`
-  Looks for the text in the *cc* property of the message.
-- **bcc**: `String|null`
-  Looks for the text in the *bcc* property of the message.
-- **subject**: `String|null`
-  Looks for the text in the *subject* property of the message.
-- **body**: `String|null`
-  Looks for the text in the *textBody* or *htmlBody* property of the message.
-- **header**: `String[]|null`
-  The array MUST contain either one or two elements. The first element is the name of the header to match against. The second (optional) element is the text to look for in the header. If not supplied, the message matches simply if it *has* a header of the given name.
-
-If zero properties are specified on the FilterCondition, the condition MUST always evaluate to `true`. If multiple properties are specified, ALL must apply for the condition to be `true` (it is equivalent to splitting the object into one-property conditions and making them all the child of an AND filter operator).
-
-The exact semantics for matching `String` fields is **deliberately not defined** to allow for flexibility in indexing implementation, subject to the following:
-
-- Text SHOULD be matched in a case-insensitive manner.
-- Text contained in either (but matched) single or double quotes SHOULD be treated as a **phrase search**, that is a match is required for that exact sequence of words, excluding the surrounding quotation marks. Use `\"`, `\'` and `\\` to match a literal `"`, `'` and `\` respectively in a phrase.
-- Outside of a phrase, white-space SHOULD be treated as dividing separate tokens that may be searched for separately in the message, but MUST all be present for the message to match the filter.
-- Tokens MAY be matched on a whole-word basis using stemming (so for example a text search for `bus` would match "buses" but not "business").
-- When searching inside the *htmlBody* property, HTML tags and attributes SHOULD be ignored.
-
-#### Sorting
-<aside class="notice">
-Only sorting on id and/or date is supported
-</aside>
-
-The `sort` argument lists the properties to compare between two messages to determine which comes first in the sort. If two messages have an identical value for the first property, the next property will be considered and so on. If all properties are the same (this includes the case where an empty array or `null` is given as the argument), the sort order is server-dependent, but MUST be stable between calls to `getMessageList`.
-
-Optionally, following the property name there can be a space and then either the string `asc` or `desc` to specify ascending or descending sort for that property. If not specified, it MUST default to **descending**.
-
-The following properties MUST be supported for sorting:
-
-- **id** - The id as returned in the Message object.
-- **date** - The date as returned in the Message object.
-
-The following properties SHOULD be supported for sorting:
-
-- **size** - The size as returned in the Message object.
-- **from** – This is taken to be either the "name" part of the Emailer object, or if none then the "email" part of the Emailer object (see the definition of the from property in the Message object). If still none, consider the value to be the empty string.
-- **to** - This is taken to be either the "name" part of the **first** Emailer object, or if none then the "email" part of the **first** Emailer object (see the definition of the to property in the Message object). If still none, consider the value to be the empty string.
-- **subject** - This is taken to be the subject of the Message with any ignoring any leading "Fwd:"s or "Re:"s (case-insensitive match).
-- **threadIsFlagged** - This value MUST be considered `true` for the message if **any** of the messages in the same thread (regardless of mailbox) have `isFlagged: true`.
-- **threadIsUnread** - This value MUST be considered `true` for the message if **any** of the messages in the same thread (regardless of mailbox) have `isUnread: true`.
-- **isFlagged** - The `isFlagged` state of the message (only).
-- **isUnread** - The `isUnread` state of the message (only).
-
-The server MAY support sorting based on other properties as well. A client can discover which properties are supported by inspecting the *capabilities* property on the Account object.
-
-The method of comparison depends on the type of the property:
-
-- `String`: Comparison function is server-dependent. It SHOULD be case-insensitive and SHOULD take into account locale-specific conventions if known for the user. However, the server MAY choose to just sort based on unicode code point, after best-effort translation to lower-case.
-- `Date`: If sorting in ascending order, the earlier date MUST come first.
-- `Boolean`: If sorting in ascending order, a `false` value MUST come before a `true` value.
-
-#### Thread collapsing
-<aside class="warning">
-Not implemented
-</aside>
-
-When `collapseThreads == true`, then after filtering and sorting the message list, the list is further winnowed by removing any messages for a thread id that has already been seen (when passing through the list sequentially). A thread will therefore only appear **once** in the `threadIds` list of the result, at the position of the first message in the list that belongs to the thread.
-
-#### Windowing
-<aside class="warning">
-Not implemented
-</aside>
-
-If a *position* offset is supplied, then this is the 0-based index of the first result to return in the list of messages after filtering, sorting and collapsing threads. If the index is greater than or equal to the total number of messages in the list, then there are no results to return, but this DOES NOT generate an error. If *position* is `null` (or, equivalently, omitted) this MUST be interpreted as `position: 0`.
-
-Alternatively, a message id, called the **anchor** may be given. In this case, after filtering, sorting and collapsing threads, the anchor is searched for in the message list. If found, the **anchor offset** is then subtracted from this index. If the resulting index is now negative, it is clamped to 0. This index is now used exactly as though it were supplied as the `position` argument. If the anchor is not found, the call is rejected with an `anchorNotFound` error.
-
-If an *anchor* is specified, any position argument supplied by the client MUST be ignored. If *anchorOffset* is `null`, it defaults to `0`. If no *anchor* is supplied, any anchor offset argument MUST be ignored.
-
-#### Response
-
-The response to a call to *getMessageList* is called *messageList*. It has the following arguments:
-
-- **accountId**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The id of the account used for the call.
-- **filter**: `FilterCondition|FilterOperator|null`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The filter of the message list. Echoed back from the call.
-- **sort**: `String[]`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  A list of Message property names used to sort by. Echoed back from the call.
-- **collapseThreads**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  Echoed back from the call.
-- **state**: `String`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  A string encoding the current state on the server. This string will change if the results of the message list MAY have changed (for example, there has been a change to the state of the set of Messages; it does not guarantee that anything in the list has changed). It may be passed to *getMessageListUpdates* to efficiently get the set of changes from the previous state.
-
-  Should a client receive back a response with a different state string to a previous call, it MUST either throw away the currently cached list and fetch it again (note, this does not require fetching the messages again, just the list of ids) or, if the server supports it, call *getMessageListUpdates* to get the delta difference.
-- **canCalculateUpdates**: `Boolean`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  This is `true` if the server supports calling `getMessageListUpdates` with these `filter`/`sort`/`collapseThreads` parameters. Note, this does not guarantee that the getMessageListUpdates call will succeed, as it may only be possible for a limited time afterwards due to server internal implementation details.
-- **position**: `Number`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The 0-based index of the first result in the `threadIds` array within the complete list.
-- **total**: `Number`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The total number of messages in the message list (given the *filter* and *collapseThreads* arguments).
-- **threadIds**: `String[]`
-  <aside class="warning">
-  Not implemented
-  </aside>
-  The list of Thread ids for each message in the list after filtering, sorting and collapsing threads, starting at the index given by the *position* argument of this response, and continuing until it hits the end of the list or reaches the `limit` number of ids.
-- **messageIds**: `String[]`
-  The list of Message ids for each message in the list after filtering, sorting and collapsing threads, starting at the index given by the *position* argument of this response, and continuing until it hits the end of the list or reaches the `limit` number of ids.
-
-The following errors may be returned instead of the `messageList` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`unsupportedSort`: Returned if the *sort* includes a property the server does not support sorting on.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`anchorNotFound`: Returned if an anchor argument was supplied, but it cannot be found in the message list.
-
-### getMessageListUpdates
-<aside class="warning">
-Not implemented
-</aside>
-
-The `getMessageListUpdates` call allows a client to efficiently update the state of any cached message list to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, the primary account will be used.
-- **filter**: `FilterCondition|FilterOperator|null`
-  The filter argument that was used with *getMessageList*.
-- **sort**: `String[]|null`
-  The sort argument that was used with *getMessageList*.
-- **collapseThreads**: `Boolean|null`
-  The *collapseThreads* argument that was used with *getMessageList*.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *messageList* response. The server will return the changes made since this state.
-- **uptoMessageId**: `String|null`
-  The message id of the last message in the list that the client knows about. In the common case of the client only having the first X ids cached, this allows the server to ignore changes further down the list the client doesn't care about.
-- **maxChanges**: `Number|null`
-  The maximum number of changes to return in the response. See below for a more detailed description.
-
-The response to *getMessageListUpdates* is called *messageListUpdates* It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **filter**: `FilterCondition|FilterOperator|null`
-  The filter of the message list. Echoed back from the call.
-- **sort**: `String[]|null`
-  A list of Message property names used to sort by. Echoed back from the call.
-- **collapseThreads**: `Boolean`
-  Echoed back from the call.
-- **oldState**: `String`
-  This is the `sinceState` argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **uptoMessageId**: `String|null`
-  Echoed back from the call.
-- **total**: `Number`
-  The total number of messages in the message list (given the filter and collapseThreads arguments).
-- **removed**: `RemovedItem[]`
-  The *messageId* and *threadId* for every message that was in the list in the old state and is not in the list in the new state. If the server cannot calculate this exactly, the server MAY return extra messages in addition that MAY have been in the old list but are not in the new list.
-
-  If an *uptoMessageId* was given AND this id was found in the list, only messages positioned before this message that were removed need be returned.
-
-  In addition, if the sort includes the property *isUnread* or *isFlagged*, the server MUST include all messages in the current list for which this property MAY have changed. If `collapseThreads == true`, then the server MUST include all messages in the current list for which this property MAY have changed **on any of the messages in the thread**.
-
-- **added**: `AddedItem[]`
-  The messageId and threadId and index in the list (in the new state) for every message that has been added to the list since the old state AND every message in the current list that was included in the *removed* array (due to a filter or sort based upon a mutable property). The array MUST be sorted in order of index, lowest index first.
-
-  If an *uptoMessageId* was given AND this id was found in the list, only messages positioned before this message that have been added need be returned.
-
-A **RemovedItem** object has the following properties:
-
-- **messageId**: `String`
-- **threadId**: `String`
-
-An **AddedItem** object has the following properties:
-
-- **messageId**: `String`
-- **threadId**: `String`
-- **index**: `Number`
-
-The result of this should be that if the client has a cached sparse array of message ids in the list in the old state:
-
-    messageIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ]
-
-then if it **splices out** all messages in the removed array:
-
-    removed = [{ messageId: "id2", … }];
-    messageIds => [ "id1", null, null, "id3", "id4", null, null, null ]
-
-and **splices in** (in order) all of the messages in the added array:
-
-    added = [{ messageId: "id5", index: 0, … }];
-    messageIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ]
-
-then the message list will now be in the new state.
-
-The following errors may be returned instead of the `messageListUpdates` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`tooManyChanges`: Returned if there are more changes the the client's *maxChanges* argument. Each item in the removed or added array is considered as one change. The client may retry with a higher max changes or invalidate its cache of the message list.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old. The client MUST invalidate its cache of the message list.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/push.mdwn b/server/protocols/jmap-draft/doc/specs/spec/push.mdwn
deleted file mode 100644
index d607c22..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/push.mdwn
+++ /dev/null
@@ -1,65 +0,0 @@
-## Push
-<aside class="warning">
-Not implemented
-</aside>
-
-Any modern email client should be able to update instantly whenever the data on the server is changed by another client or message delivery. Push notifications in JMAP occur out-of-band (i.e. not over the same connection as API exchanges) so that they can make use of efficient native push mechanisms on different platforms.
-
-The general model for push is simple and does not send any sensitive data over the push channel, making it suitable for use with less trusted 3rd party intermediaries. The format allows multiple changes to be coalesced into a single push update, and the frequency of pushes to be rate limited by the server. It doesn't matter if some push events are dropped before they reach the client; it will still get all changes next time it syncs.
-
-When something changes on the server, the server pushes a small JSON object to the client with the following property:
-
-- **changed**: `String[ChangedStates]`
-  A map of *account id* to an object encoding the state of data types which have changed for that account since the last push event, for each of the accounts to which the user has access and for which something has changed.
-
-A **ChangedStates** object is a map of the type name (e.g. "Mailbox" or "Message") to the current state token for that type (i.e. the "state" property that would currently be returned by a call to "getMailboxes" or "getMessages", as appropriate). The types in JMAP are "Mailbox", "Thread", "Message", "ContactGroup", "Contact", "Calendar", "CalendarEvent".
-
-Upon receiving this data, the client can compare the new state strings with its current values to see whether it has the current data for these types. The actual changes can then be efficiently fetched in a single standard API request (using the *getFooUpdates* type methods).
-
-### Event Source
-
-There are two mechanisms by which the client can receive the push events. The first is directly via a `text/event-stream` resource, as described in
-<http://www.w3.org/TR/eventsource/>. This is essentially a long running HTTP request down which the server can push data. When a change occurs, the server MUST push an event called **state** to any connected clients.
-
-The server MAY also set a new `Last-Event-Id` that encodes the entire server state visible to the user. When a new connection is made to the event-source endpoint, the server can then work out whether the client has missed some changes which it should send immediately.
-
-The server MUST also send an event called **ping** with an empty object as the data if a maximum of 5 minutes has elapsed since the previous event. This MUST NOT set a new `Last-Event-Id`. A client may detect the absence of these to determine that the HTTP connection has been dropped somewhere along the route and so it needs to re-establish the connection.
-
-Refer to the Authentication section of this spec for details on how to get the URL for the event-source endpoint. The request must be authenticated using an `Authorization` header like any HTTP request.
-
-A client MAY hold open multiple connections to the event-source, although it SHOULD try to use a single connection for efficiency.
-
-### setPushCallback
-
-The second push mechanism is to register a callback URL to which the JMAP server will make an HTTPS POST request whenever the event occurs. The request MUST have a content type of `application/json` and contain the same UTF-8 JSON encoded object as described above as the body.
-
-The JMAP server MUST also set the following headers in the POST request:
-- `X-JMAP-EventType: state`
-- `X-JMAP-User: ${username}` where `${username}` is the username of the authenticated user for which the push event occurred.
-
-The JMAP server MUST follow any redirects. If the final response code from the server is `2xx`, the callback is considered a success. If the response code is `503` (Service Unavailable), the JMAP server MAY try again later (but may also just drop the event). If the response code is `429` (Too Many Requests) the JMAP server SHOULD attempt to reduce the frequency of pushes to that URL. Any other response code SHOULD be considered a **permanent failure** and the callback should be deregistered (not tried again even for future events unless explicitly re-registered by the client).
-
-The URL set by the client MUST use the HTTPS protocol and SHOULD encode within it a unique token that can be verified by the server to know that the request comes from the JMAP server the authenticated client connected to.
-
-The callback is tied to the access token used to create it. Should the access token expire or be revoked, the callback MUST be removed by the JMAP server. The client MUST re-register the callback after reauthenticating to resume callbacks.
-
-Each session may only have a single callback URL registered. To set it, make a call to *setPushCallback*. It takes the following argument:
-
-- **callback**: `String|null`
-  The (HTTPS) URL the JMAP server should POST events to. This will replace any previously set URL. Set to `null` to just remove any previously set callback URL.
-
-The response to *setPushCallback* is called *pushCallbackSet*. It has the following argument:
-
-- **callback**: `String|null`
-  Echoed back from the call.
-
-The following error may be returned instead of the *mailboxesSet* response:
-
-`invalidUrl`: Returned if the URL does not begin with `https://`, or is otherwise syntactically invalid or does not resolve.
-
-### getPushCallback
-
-To check the currently set callback URL (if any), make a call to *getPushCallback*. It does not take any arguments. The response to *getPushCallback* is called `pushCallback`. It has a single argument:
-
-- **callback**: `String|null`
-  The URL the JMAP server is currently posting push events to, or `null` if none.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/searchsnippet.mdwn b/server/protocols/jmap-draft/doc/specs/spec/searchsnippet.mdwn
deleted file mode 100644
index d870ee4..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/searchsnippet.mdwn
+++ /dev/null
@@ -1,44 +0,0 @@
-## SearchSnippets
-<aside class="warning">
-Not implemented
-</aside>
-
-When doing a search on a `String` property, the client may wish to show the relevant section of the body that matches the search as a preview instead of the beginning of the message, and to highlight any matching terms in both this and the subject of the message. Search snippets represent this data.
-
-A **SearchSnippet** object has the following properties:
-
-- **messageId**: `String`
-  The message id the snippet applies to.
-- **subject**: `String|null`
-  If text from the filter matches the subject, this is the subject of the message HTML-escaped, with matching words/phrases wrapped in `<mark></mark>` tags. If it does not match, this is `null`.
-- **preview**: `String|null`
-  If text from the filter matches the plain-text or HTML body, this is the relevant section of the body (converted to plain text if originally HTML), HTML-escaped, with matching words/phrases wrapped in `<mark></mark>` tags, up to 256 characters long. If it does not match, this is `null`.
-
-It is server-defined what is a relevant section of the body for preview. If the server is unable to determine search snippets, it MUST just return `null` for both the *subject* and *preview* properties.
-
-Note, unlike most data types, a SearchSnippet DOES NOT have a property called `id`.
-
-### getSearchSnippets
-
-To fetch search snippets, make a call to `getSearchSnippets`. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If `null`, defaults to the primary account.
-- **messageIds**: `String[]`
-  The list of ids of messages to fetch the snippets for.
-- **filter**: `FilterCondition|FilterOperator|null`
-  The same filter as passed to getMessageList; see the description of this method for details.
-
-The response to `getSearchSnippets` is called `searchSnippets`. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **filter**: `FilterOperator`
-  Echoed back from the call.
-- **list**: `SearchSnippet[]`
-  An array of SearchSnippets objects for the requested message ids. This may not be in the same order as the ids that were in the request.
-- **notFound**: `String[]|null`
-  An array of message ids requested which could not be found, or `null` if all
-  ids were found.
-
-Since snippets are only based on immutable properties, there is no state string or update mechanism needed.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/thread.mdwn b/server/protocols/jmap-draft/doc/specs/spec/thread.mdwn
deleted file mode 100644
index 10d6ad9..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/thread.mdwn
+++ /dev/null
@@ -1,123 +0,0 @@
-## Threads
-<aside class="warning">
-Not implemented
-</aside>
-
-Replies are grouped together with the original message to form a thread. In JMAP, a thread is simply a flat list of messages, ordered by date. Every message MUST belong to a thread, even if it is the only message in the thread.
-
-The JMAP spec does not require the server to use any particular algorithm for determining whether two messages belong to the same thread, however there is a recommended algorithm in the [implementation guide](server.html).
-
-If messages are delivered out of order for some reason, a user may receive two messages in the same thread but without headers that associate them with each other. The arrival of a third message in the thread may provide the missing references to join them all together into a single thread. Since the `threadId` of a message is immutable, if the server wishes to merge the threads, it MUST handle this by deleting and reinserting (with a new message id) the messages that change threadId.
-
-A **Thread** object has the following properties:
-
-- **id**: `String`
-  The id of the thread. This property is immutable.
-- **messageIds**: `String[]`
-  The ids of the messages in the thread, sorted such that:
-  - Any message with `isDraft == true` and an *inReplyToMessageId* property that corresponds to another message in the thread comes immediately after that message in the sort order.
-  - Other than that, everything is sorted in date order (the same as the *date* property on the Message object), oldest first.
-
-### getThreads
-
-Threads can only be fetched explicitly by id. To fetch threads, make a call to *getThreads*. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **ids**: `String[]`
-  An array of ids for the threads to fetch.
-- **fetchMessages**: `Boolean|null`
-  If true, after outputting a *threads* response, an implicit call will be made to *getMessages* with a list of all message ids in the returned threads as the *ids* argument, and the *fetchMessageProperties* argument as the *properties* argument. If `false` or `null`, no implicit call will be made.
-- **fetchMessageProperties**: `String[]|null`
-  The list of properties to fetch on any fetched messages. See *getMessages* for a full description.
-
-The response to *getThreads* is called *threads*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **state**: `String`
-  A string encoding the current state on the server. This string will change
-  if any threads change (that is, new messages arrive, or messages are deleted, as these are the only two events that change thread membership). It can be passed to *getThreadUpdates* to efficiently get the list of changes from the previous state.
-- **list**: `Thread[]`
-  An array of Thread objects for the requested thread ids. This may not be in the same order as the ids were in the request.
-- **notFound**: `String[]|null`
-  An array of thread ids requested which could not be found, or `null` if all ids were found.
-
-The following errors may be returned instead of the `threads` response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-Example of a successful request:
-
-    [ "getThreads", {
-      "ids": ["f123u4", "f41u44"],
-      "fetchMessages": false,
-      "fetchMessageProperties": null
-    }, "#1" ]
-
-and response:
-
-    [ "threads", {
-      "state": "f6a7e214",
-      "list": [
-        {
-          "id": "f123u4",
-          "messageIds": [ "eaa623", "f782cbb"]
-        },
-        {
-          "id": "f41u44",
-          "messageIds": [ "82cf7bb" ]
-        }
-      ],
-      "notFound": null
-    }, "#1" ]
-
-
-### getThreadUpdates
-
-When messages are created or deleted, new threads may be created, or the set of messages belonging to an existing thread may change. If a call to *getThreads* returns with a different *state* string in the response to a previous call, the state of the threads has changed on the server and the client needs to work out which part of its cache is now invalid.
-
-The *getThreadUpdates* call allows a client to efficiently update the state of any cached threads to match the new state on the server. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The id of the account to use for this call. If not given, defaults to the primary account.
-- **sinceState**: `String`
-  The current state of the client. This is the string that was returned as the *state* argument in the *threads* response. The server will return the changes made since this state.
-- **maxChanges**: `Number|null`
-  The maximum number of Thread ids to return in the response. The server MAY choose to clamp this value to a particular maximum or set a maximum if none is given by the client. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
-- **fetchRecords**: `Boolean|null`
-  If `true`, after outputting a *threadUpdates* response, an implicit call will be made to *getThreads* with the *changed* property of the response as the *ids* argument, and *fetchMessages* equal to `false`.
-
-The response to *getThreadUpdates* is called *threadUpdates*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **oldState**: `String`
-  This is the *sinceState* argument echoed back; the state from which the server is returning changes.
-- **newState**: `String`
-  This is the state the client will be in after applying the set of changes to the old state.
-- **hasMoreUpdates**: `Boolean`
-  If `true`, the client may call *getThreadUpdates* again with the *newState* returned to get further updates. If `false`, *newState* is the current server state.
-- **changed**: `String[]`
-  An array of thread ids where the list of messages within the thread has
-  changed between the old state and the new state, and the thread currently has at least one message in it.
-- **removed**: `String[]`
-  An array of thread ids where the list of messages within the thread has changed since the old state, and there are now no messages in the thread.
-
-If a *maxChanges* is supplied, or set automatically by the server, the server must try to limit the number of ids across *changed* and *removed* to the number given. If there are more changes than this between the client's state and the current server state, the update returned MUST take the client to an intermediate state, from which the client can continue to call *getThreadUpdates* until it is fully up to date. The server MAY return more ids than the *maxChanges* total if this is required for it to be able to produce an update to an intermediate state, but it SHOULD try to keep it close to the maximum requested.
-
-If a thread has been modified AND deleted since the oldState, the server SHOULD just return the id in the *removed* response, but MAY return it in the changed response as well. If a thread has been created AND deleted since the oldState, the server should remove the thread id from the response entirely, but MAY include it in the *removed* response, and optionally the *changed* response as well.
-
-The following errors may be returned instead of the *threadUpdates* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if the request does not include one of the required arguments, or one of the arguments is of the wrong type, or otherwise invalid. A *description* property MAY be present on the response object to help debug with an explanation of what the problem was.
-
-`cannotCalculateChanges`: Returned if the server cannot calculate the changes from the state string given by the client. Usually due to the client's state being too old, or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its Thread cache. The error object MUST also include a `newState: String` property with the current state for the type.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/upload.mdwn b/server/protocols/jmap-draft/doc/specs/spec/upload.mdwn
deleted file mode 100644
index 15a54b2..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/upload.mdwn
+++ /dev/null
@@ -1,53 +0,0 @@
-## File Uploads
-<aside class="warning">
-Not implemented
-</aside>
-
-There is a single endpoint which handles all file uploads, regardless of what they are to be used for. To upload a file, submit a POST request to the file upload endpoint (see the authentication section for information on how to obtain this URL). The Content-Type MUST be correctly set for the type of the file being uploaded. The request MUST be authenticated as per any HTTP request. The request MAY include an "X-JMAP-AccountId" header, with the value being the account to use for the request. Otherwise, the default account will be used.
-
-The server will respond with one of the following HTTP response codes:
-
-#### `201`: File uploaded successfully
-
-The content of the response is a single JSON object with the following properties:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **blobId**: `String`,
-  The id representing the binary data uploaded. The data for this id is immutable. The id *only* refers to the binary data, not any metadata.
-- **type**: `String`
-  The content type of the file.
-- **size**: `Number`
-  The size of the file in bytes.
-- **expires**: `Date`
-  The date the file will be deleted from temp storage if not referenced by another object, e.g. used in a draft.
-
-Once the file has been used, for example attached to a draft message, the file will no longer expire, and is instead guaranteed to exist while at least one other object references it. Once no other object references it, the server MAY immediately delete the file at any time. It MUST NOT delete the file during the method call which removed the last reference, so that if there is a create and a delete within the same call which both reference the file, this always works.
-
-If uploading a file would take the user over quota, the server SHOULD delete previously uploaded (but unused) files before their expiry time. This means a client does not have to explicitly delete unused temporary files (indeed, there is no way for it to do so).
-
-If identical binary content is uploaded, the same *blobId* SHOULD be returned.
-
-#### `400`: Bad request
-
-The request was malformed (this includes the case where an `X-JMAP-AccountId` header is sent with a value that does not exist). The client SHOULD NOT retry the same request.
-
-#### `401`: Unauthorized
-
-The `Authorization` header was missing or did not contain a valid token. Reauthenticate and then retry the request. There is no content in the response.
-
-#### `404`: Not Found
-
-The upload endpoint has moved. See the Authentication section of the spec for how to rediscover the current URL to use. There is no content in the response.
-
-#### `413`:  Request Entity Too Large
-
-The file is larger than the maximum size the server is willing to accept for a single file. The client SHOULD NOT retry uploading the same file. There is no content in the response. The client may discover the maximum size the server is prepared to accept by inspecting the capabilities property of the Account object.
-
-#### `415`: Unsupported Media Type
-
-The server MAY choose to not allow certain content types to be uploaded, such as executable files. This error response is returned if an unacceptable type is uploaded. The client SHOULD NOT retry uploading the same file. There is no content in the response.
-
-#### `503`: Service Unavailable
-
-The server is currently down. The client should try again later with exponential backoff. There is no content in the response.
diff --git a/server/protocols/jmap-draft/doc/specs/spec/vacationresponse.mdwn b/server/protocols/jmap-draft/doc/specs/spec/vacationresponse.mdwn
deleted file mode 100644
index 255c9f6..0000000
--- a/server/protocols/jmap-draft/doc/specs/spec/vacationresponse.mdwn
+++ /dev/null
@@ -1,71 +0,0 @@
-## Vacation Response
-
-The **VacationResponse** object represents the state of vacation-response
-related settings for an account. It has the following properties:
-
-- **id**: `String`
-  The id of the object. This property is immutable. There is only ever one
-  vacation response object, and its id is `"singleton"`.
-- **isEnabled** `Boolean`
-  Is the vacation response enabled?
-- **fromDate**: `Date|null`
-  If *isEnabled* is `true`, the date/time after which messages that arrive should receive the user's vacation response, in UTC. If `null`, the vacation response is effective immediately.
-- **toDate**: `Date|null`
-  If *isEnabled* is `true`, the date/time after which messages that arrive should no longer receive the user's vacation response, in UTC. If `null`, the vacation response is effective indefinitely.
-- **textBody**: `String`
-  The plain text message to send in response to messages when the vacation response is enabled.
-
-### getVacationResponse
-
-There MUST only be exactly one VacationResponse object in an account. It MUST have the id `"singleton"`.
-
-To fetch the vacation response object, make a call to `getVacationResponse`. It takes the following argument:
-
-- **accountId**: `String|null`
-  The Account to get the vacation response for. If `null`, the primary account is used.
-
-The response to *getVacationResponse* is called *vacationResponse*. It has the following arguments:
-
-- **accountId**: `String`
-  The id of the account used for the call.
-- **list**: `VacationResponse[]`
-  An array containing the single VacationResponse object.
-
-The following errors may be returned instead of the *vacationResponse* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-### setVacationResponse
-
-Sets properties on the vacation response object. It takes the following arguments:
-
-- **accountId**: `String|null`
-  The Account to set the vacation response for. If `null`, the primary account is used.
-- **update**: `String[VacationResponse]|null`
-  A map of id ("singleton") to the VacationResponse object with new values for the properties you wish to change. The object may omit any property; only properties that have changed need be included.
-
-If any of the properties in the update are invalid (immutable and different to the current server value, wrong type), the server MUST reject the update with a SetError of type `invalidProperties`. The SetError object SHOULD contain a property called *properties* of type `String[]` that lists **all** the properties that were invalid. The object MAY also contain a *description* property of type `String` with a user-friendly description of the problems.
-
-The response is called *vacationResponseSet*. It has the following arguments:
-
-- **updated**: `String[]`
-  Contains the single id ("singleton") if the vacation response was successfully updated.
-- **notUpdated**: `String[SetError]`
-  A map of id ("singleton") to a SetError object if the update failed.
-
-A **SetError** object has the following properties:
-
-- **type**: `String`
-  The type of error.
-- **description**: `String|null`
-  A description of the error to display to the user.
-
-The following errors may be returned instead of the *vacationResponseSet* response:
-
-`accountNotFound`: Returned if an *accountId* was explicitly included with the request, but it does not correspond to a valid account.
-
-`accountNoMail`: Returned if the *accountId* given corresponds to a valid account, but does not contain any mail data.
-
-`invalidArguments`: Returned if one of the arguments is of the wrong type, or otherwise invalid (including using an id other than `"singleton"`). A `description` property MAY be present on the response object to help debug with an explanation of what the problem was.
diff --git a/server/protocols/jmap-draft/pom.xml b/server/protocols/jmap-draft/pom.xml
deleted file mode 100644
index db324dd..0000000
--- a/server/protocols/jmap-draft/pom.xml
+++ /dev/null
@@ -1,328 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-    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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.apache.james</groupId>
-        <artifactId>james-server</artifactId>
-        <version>3.9.0-SNAPSHOT</version>
-        <relativePath>../../pom.xml</relativePath>
-    </parent>
-
-    <artifactId>james-server-jmap-draft</artifactId>
-    <packaging>jar</packaging>
-
-    <name>Apache James :: Server :: JMAP (Draft)</name>
-
-    <properties>
-        <zalando.version>1.13.0</zalando.version>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-mailbox-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-mailbox-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-mailbox-memory</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-mailbox-memory</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>apache-mailet-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>blob-memory</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>blob-storage-strategy</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>event-bus-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>event-sourcing-event-store-memory</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-mdn</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-data-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-data-jmap</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-data-memory</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-dnsservice-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-filesystem-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-jmap</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-jmap-rfc-8621</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-jmap-rfc-8621</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-jwt</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-lifecycle-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-queue-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-queue-memory</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-testing</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-util</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-util</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>metrics-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>metrics-logger</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>metrics-tests</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
-            <artifactId>testing-base</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-datatype-guava</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-datatype-jdk8</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-datatype-jsr310</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.github.dpaukov</groupId>
-            <artifactId>combinatoricslib3</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.github.fge</groupId>
-            <artifactId>throwing-lambdas</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.jayway.jsonpath</groupId>
-            <artifactId>json-path</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.github.hakky54</groupId>
-            <artifactId>sslcontext-kickstart-for-pem</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.jsonwebtoken</groupId>
-            <artifactId>jjwt-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.jsonwebtoken</groupId>
-            <artifactId>jjwt-impl</artifactId>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.jsonwebtoken</groupId>
-            <artifactId>jjwt-jackson</artifactId>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.projectreactor</groupId>
-            <artifactId>reactor-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.projectreactor.netty</groupId>
-            <artifactId>reactor-netty</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.rest-assured</groupId>
-            <artifactId>rest-assured</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.vavr</groupId>
-            <artifactId>vavr</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.inject</groupId>
-            <artifactId>jakarta.inject-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>net.javacrumbs.json-unit</groupId>
-            <artifactId>json-unit-assertj</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.james</groupId>
-            <artifactId>apache-mime4j-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.james</groupId>
-            <artifactId>apache-mime4j-dom</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.awaitility</groupId>
-            <artifactId>awaitility</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.jgrapht</groupId>
-            <artifactId>jgrapht-core</artifactId>
-            <version>1.5.2</version>
-        </dependency>
-        <dependency>
-            <groupId>org.jsoup</groupId>
-            <artifactId>jsoup</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.junit.platform</groupId>
-            <artifactId>junit-platform-engine</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>${james.groupId}</groupId>
-                <artifactId>mailetdocs-maven-plugin</artifactId>
-                <version>${project.version}</version>
-                <configuration>
-                    <outputDirectory>target/mailetdocs</outputDirectory>
-                </configuration>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>mailetdocs</goal>
-                        </goals>
-                        <phase>package</phase>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <reuseForks>true</reuseForks>
-                    <forkCount>1C</forkCount>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-</project>
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPDraftConfiguration.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPDraftConfiguration.java
deleted file mode 100644
index 1635b6b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPDraftConfiguration.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class JMAPDraftConfiguration {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private Optional<String> keystore = Optional.empty();
-        private Optional<String> keystoreType = Optional.empty();
-        private Optional<String> privateKey = Optional.empty();
-        private Optional<String> certificates = Optional.empty();
-        private Optional<String> secret = Optional.empty();
-        private Optional<Boolean> enabled = Optional.empty();
-        private ImmutableList.Builder<String> jwtPublicKeyPem = ImmutableList.builder();
-        private Optional<List<String>> authenticationStrategies = Optional.empty();
-
-        private Builder() {
-
-        }
-
-        public Builder keystore(String keystore) {
-            this.keystore = Optional.ofNullable(keystore)
-                .filter(s -> !s.isEmpty());
-            return this;
-        }
-
-        public Builder privateKey(String privateKey) {
-            this.privateKey = Optional.ofNullable(privateKey);
-            return this;
-        }
-
-        public Builder certificates(String certificates) {
-            this.certificates = Optional.ofNullable(certificates);
-            return this;
-        }
-
-        public Builder keystoreType(String keystoreType) {
-            this.keystoreType = Optional.ofNullable(keystoreType);
-            return this;
-        }
-
-        public Builder enabled(boolean enabled) {
-            this.enabled = Optional.of(enabled);
-            return this;
-        }
-
-        public Builder enable() {
-            return enabled(true);
-        }
-
-        public Builder disable() {
-            return enabled(false);
-        }
-
-        public Builder secret(String secret) {
-            this.secret = Optional.ofNullable(secret)
-                .filter(s -> !s.isEmpty());
-            return this;
-        }
-
-        public Builder jwtPublicKeyPem(Collection<String> jwtPublicKeyPem) {
-            Preconditions.checkNotNull(jwtPublicKeyPem);
-            this.jwtPublicKeyPem.addAll(jwtPublicKeyPem);
-            return this;
-        }
-
-        public Builder authenticationStrategies(Optional<List<String>> authenticationStrategies) {
-            this.authenticationStrategies = authenticationStrategies;
-            return this;
-        }
-
-        public JMAPDraftConfiguration build() {
-            Preconditions.checkState(enabled.isPresent(), "You should specify if JMAP server should be started");
-
-            Preconditions.checkState(!enabled.get() || cryptoParametersAreSpecified(),
-                "('keystore' && 'secret') or (privateKey && certificates) is mandatory");
-            return new JMAPDraftConfiguration(enabled.get(), keystore, privateKey, certificates, keystoreType.orElse("JKS"),
-                secret, jwtPublicKeyPem.build(), authenticationStrategies);
-        }
-
-        private boolean cryptoParametersAreSpecified() {
-            return (keystore.isPresent() && secret.isPresent())
-                || (privateKey.isPresent() && certificates.isPresent());
-        }
-    }
-
-    private final boolean enabled;
-    private final Optional<String> keystore;
-    private final Optional<String> privateKey;
-    private final Optional<String> certificates;
-    private final String keystoreType;
-    private final Optional<String> secret;
-    private final List<String> jwtPublicKeyPem;
-    private final Optional<List<String>> authenticationStrategies;
-
-    @VisibleForTesting
-    JMAPDraftConfiguration(boolean enabled, Optional<String> keystore, Optional<String> privateKey, Optional<String> certificates, String keystoreType,
-                           Optional<String> secret, List<String> jwtPublicKeyPem, Optional<List<String>> authenticationStrategies) {
-        this.enabled = enabled;
-        this.keystore = keystore;
-        this.privateKey = privateKey;
-        this.certificates = certificates;
-        this.keystoreType = keystoreType;
-        this.secret = secret;
-        this.jwtPublicKeyPem = jwtPublicKeyPem;
-        this.authenticationStrategies = authenticationStrategies;
-    }
-
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    public Optional<String> getKeystore() {
-        return keystore;
-    }
-
-    public String getKeystoreType() {
-        return keystoreType;
-    }
-
-    public Optional<String> getPrivateKey() {
-        return privateKey;
-    }
-
-    public Optional<String> getCertificates() {
-        return certificates;
-    }
-
-    public Optional<String> getSecret() {
-        return secret;
-    }
-
-    public List<String> getJwtPublicKeyPem() {
-        return jwtPublicKeyPem;
-    }
-
-    public Optional<List<String>> getAuthenticationStrategies() {
-        return authenticationStrategies;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/AccessTokenManager.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/AccessTokenManager.java
deleted file mode 100644
index f42b2f3..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/AccessTokenManager.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.api;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
-import org.reactivestreams.Publisher;
-
-public interface AccessTokenManager {
-
-    Publisher<AccessToken> grantAccessToken(Username username);
-
-    Publisher<Username> getUsernameFromToken(AccessToken token) throws InvalidAccessToken;
-
-    Publisher<Boolean> isValid(AccessToken token);
-
-    Publisher<Void> revoke(AccessToken token);
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenFactory.java
deleted file mode 100644
index b852b84..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.api;
-
-import org.apache.james.jmap.draft.model.AttachmentAccessToken;
-import org.apache.james.jmap.draft.model.ContinuationToken;
-
-public interface SimpleTokenFactory {
-    ContinuationToken generateContinuationToken(String username);
-
-    AttachmentAccessToken generateAttachmentAccessToken(String username, String blobId);
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenManager.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenManager.java
deleted file mode 100644
index 0dbfb21..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/api/SimpleTokenManager.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.api;
-
-import org.apache.james.jmap.draft.model.SignedExpiringToken;
-
-public interface SimpleTokenManager {
-    enum TokenStatus {
-        OK,
-        INVALID,
-        EXPIRED
-    }
-
-    TokenStatus getValidity(SignedExpiringToken token);
-
-    boolean isValid(SignedExpiringToken token);
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImpl.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImpl.java
deleted file mode 100644
index ab0f7bf..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImpl.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.AccessTokenRepository;
-import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
-import org.apache.james.jmap.draft.api.AccessTokenManager;
-
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Mono;
-
-public class AccessTokenManagerImpl implements AccessTokenManager {
-
-    private final AccessTokenRepository accessTokenRepository;
-
-    @Inject
-    AccessTokenManagerImpl(AccessTokenRepository accessTokenRepository) {
-        this.accessTokenRepository = accessTokenRepository;
-    }
-
-    @Override
-    public Mono<AccessToken> grantAccessToken(Username username) {
-        Preconditions.checkNotNull(username);
-        AccessToken accessToken = AccessToken.generate();
-
-        return accessTokenRepository.addToken(username, accessToken)
-            .thenReturn(accessToken);
-    }
-
-    @Override
-    public Mono<Username> getUsernameFromToken(AccessToken token) throws InvalidAccessToken {
-        return accessTokenRepository.getUsernameFromToken(token);
-    }
-    
-    @Override
-    public Mono<Boolean> isValid(AccessToken token) throws InvalidAccessToken {
-        try {
-            return getUsernameFromToken(token)
-                .thenReturn(true);
-        } catch (InvalidAccessToken e) {
-            return Mono.just(false);
-        }
-    }
-
-    @Override
-    public Mono<Void> revoke(AccessToken token) {
-        return accessTokenRepository.removeToken(token);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AsymmetricKeys.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AsymmetricKeys.java
deleted file mode 100644
index fbf082c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/AsymmetricKeys.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-class AsymmetricKeys {
-
-    private final PrivateKey privateKey;
-    private final PublicKey publicKey;
-
-    AsymmetricKeys(PrivateKey privateKey, PublicKey publicKey) {
-        this.privateKey = privateKey;
-        this.publicKey = publicKey;
-    }
-
-    PrivateKey getPrivateKey() {
-        return privateKey;
-    }
-
-    PublicKey getPublicKey() {
-        return publicKey;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandler.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandler.java
deleted file mode 100644
index 5368f21..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandler.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Base64;
-
-import jakarta.inject.Inject;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-public class JamesSignatureHandler implements SignatureHandler {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(JamesSignatureHandler.class);
-
-    public static final String ALGORITHM = "SHA1withRSA";
-
-    private final SecurityKeyLoader keyLoader;
-
-    private AsymmetricKeys securityKeys;
-
-
-    @Inject
-    @VisibleForTesting JamesSignatureHandler(SecurityKeyLoader keyLoader) {
-        this.keyLoader = keyLoader;
-    }
-
-    @Override
-    public void init() throws Exception {
-        securityKeys = keyLoader.load();
-    }
-
-    @Override
-    public String sign(String source) {
-        Preconditions.checkNotNull(source);
-        try {
-            Signature javaSignature = Signature.getInstance(ALGORITHM);
-            javaSignature.initSign(securityKeys.getPrivateKey());
-            javaSignature.update(source.getBytes());
-            return Base64.getEncoder().encodeToString(javaSignature.sign());
-        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public boolean verify(String source, String signature) {
-        Preconditions.checkNotNull(source);
-        Preconditions.checkNotNull(signature);
-        try {
-            Signature javaSignature = Signature.getInstance(ALGORITHM);
-            javaSignature.initVerify(securityKeys.getPublicKey());
-            javaSignature.update(source.getBytes());
-            return javaSignature.verify(Base64.getDecoder().decode(signature));
-        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
-            throw new RuntimeException(e);
-        } catch (SignatureException e) {
-            LOGGER.warn("Attempt to use a malformed signature '{}' for source '{}'", signature, source, e);
-            return false;
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoader.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoader.java
deleted file mode 100644
index 9e6c004..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoader.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.Key;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.james.filesystem.api.FileSystem;
-import org.apache.james.jmap.draft.JMAPDraftConfiguration;
-import org.apache.james.jwt.PublicKeyReader;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import nl.altindag.ssl.pem.exception.PemParseException;
-import nl.altindag.ssl.pem.util.PemUtils;
-import nl.altindag.ssl.util.KeyStoreUtils;
-
-public class SecurityKeyLoader {
-    private static final String ALIAS = "james";
-
-    private final FileSystem fileSystem;
-    private final JMAPDraftConfiguration jmapDraftConfiguration;
-
-    @VisibleForTesting
-    @Inject
-    SecurityKeyLoader(FileSystem fileSystem, JMAPDraftConfiguration jmapDraftConfiguration) {
-        this.fileSystem = fileSystem;
-        this.jmapDraftConfiguration = jmapDraftConfiguration;
-    }
-
-    public AsymmetricKeys load() throws Exception {
-        Preconditions.checkState(jmapDraftConfiguration.isEnabled(), "JMAP is not enabled");
-
-        if (jmapDraftConfiguration.getKeystore().isPresent()) {
-            return loadFromKeystore();
-        }
-        return loadFromPEM();
-    }
-
-    private AsymmetricKeys loadFromKeystore() throws Exception {
-        Preconditions.checkState(jmapDraftConfiguration.getKeystore().isPresent());
-        Preconditions.checkState(jmapDraftConfiguration.getSecret().isPresent());
-
-        char[] secret = jmapDraftConfiguration.getSecret().get().toCharArray();
-        KeyStore keystore = KeyStoreUtils.loadKeyStore(fileSystem.getResource(jmapDraftConfiguration.getKeystore().get()), secret);
-
-        Certificate aliasCertificate = Optional
-            .ofNullable(keystore.getCertificate(ALIAS))
-            .orElseThrow(() -> new KeyStoreException("Alias '" + ALIAS + "' keystore can't be found"));
-
-        PublicKey publicKey = aliasCertificate.getPublicKey();
-        Key key = keystore.getKey(ALIAS, secret);
-        if (! (key instanceof PrivateKey)) {
-            throw new KeyStoreException("Provided key is not a PrivateKey");
-        }
-        return new AsymmetricKeys((PrivateKey) key, publicKey);
-    }
-
-    private AsymmetricKeys loadFromPEM() throws Exception {
-        Preconditions.checkState(jmapDraftConfiguration.getCertificates().isPresent());
-        Preconditions.checkState(jmapDraftConfiguration.getPrivateKey().isPresent());
-
-        PrivateKey privateKey = PemUtils.loadPrivateKey(
-            fileSystem.getResource(jmapDraftConfiguration.getPrivateKey().get()),
-            jmapDraftConfiguration.getSecret()
-                .map(String::toCharArray)
-                .orElse(null));
-
-        return new AsymmetricKeys(privateKey, loadPublicKey());
-    }
-
-    private PublicKey loadPublicKey() throws IOException {
-        try {
-            X509Certificate certificate = PemUtils.loadCertificate(
-                fileSystem.getResource(jmapDraftConfiguration.getCertificates().get()))
-                .get(0);
-            return certificate.getPublicKey();
-        } catch (PemParseException e) {
-            String publicKeyAsString = IOUtils.toString(fileSystem.getResource(jmapDraftConfiguration.getCertificates().get()), StandardCharsets.US_ASCII);
-            return new PublicKeyReader()
-                .fromPEM(publicKeyAsString)
-                .orElseThrow(() -> new IllegalArgumentException("Key must either be a valid certificate or a public key"));
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignatureHandler.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignatureHandler.java
deleted file mode 100644
index 0bc4e8c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignatureHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-public interface SignatureHandler {
-    
-    void init() throws Exception;
-
-    String sign(String source);
-
-    boolean verify(String source, String signature);
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenFactory.java
deleted file mode 100644
index 17e25c1..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenFactory.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.api.SimpleTokenFactory;
-import org.apache.james.jmap.draft.model.AttachmentAccessToken;
-import org.apache.james.jmap.draft.model.ContinuationToken;
-import org.apache.james.util.date.ZonedDateTimeProvider;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-
-public class SignedTokenFactory implements SimpleTokenFactory {
-
-    private final SignatureHandler signatureHandler;
-    private final ZonedDateTimeProvider zonedDateTimeProvider;
-
-    @Inject
-    public SignedTokenFactory(SignatureHandler signatureHandler, ZonedDateTimeProvider zonedDateTimeProvider) {
-        this.signatureHandler = signatureHandler;
-        this.zonedDateTimeProvider = zonedDateTimeProvider;
-    }
-
-    @Override
-    public ContinuationToken generateContinuationToken(String username) {
-        Preconditions.checkNotNull(username);
-        ZonedDateTime expirationTime = zonedDateTimeProvider.get().plusMinutes(15);
-        return new ContinuationToken(username,
-            expirationTime,
-            signatureHandler.sign(
-                    Joiner.on(ContinuationToken.SEPARATOR)
-                        .join(username,
-                            DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(expirationTime))));
-    }
-
-    @Override
-    public AttachmentAccessToken generateAttachmentAccessToken(String username, String blobId) {
-        Preconditions.checkArgument(! Strings.isNullOrEmpty(blobId));
-        ZonedDateTime expirationTime = zonedDateTimeProvider.get().plusMinutes(5);
-        return AttachmentAccessToken.builder()
-                .username(username)
-                .blobId(blobId)
-                .expirationDate(expirationTime)
-                .signature(signatureHandler.sign(Joiner.on(AttachmentAccessToken.SEPARATOR)
-                                                    .join(blobId,
-                                                            username, 
-                                                            DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(expirationTime))))
-                .build();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenManager.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenManager.java
deleted file mode 100644
index 9ff4233..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/crypto/SignedTokenManager.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.api.SimpleTokenManager;
-import org.apache.james.jmap.draft.model.SignedExpiringToken;
-import org.apache.james.util.date.ZonedDateTimeProvider;
-
-import com.google.common.base.Preconditions;
-
-public class SignedTokenManager implements SimpleTokenManager {
-
-    private final SignatureHandler signatureHandler;
-    private final ZonedDateTimeProvider zonedDateTimeProvider;
-
-    @Inject
-    public SignedTokenManager(SignatureHandler signatureHandler, ZonedDateTimeProvider zonedDateTimeProvider) {
-        this.signatureHandler = signatureHandler;
-        this.zonedDateTimeProvider = zonedDateTimeProvider;
-    }
-
-    @Override
-    public TokenStatus getValidity(SignedExpiringToken token) {
-        Preconditions.checkNotNull(token);
-        if (! isCorrectlySigned(token)) {
-            return TokenStatus.INVALID;
-        }
-        if (isExpired(token)) {
-            return TokenStatus.EXPIRED;
-        }
-        return TokenStatus.OK;
-    }
-    
-    @Override
-    public boolean isValid(SignedExpiringToken token) {
-        Preconditions.checkNotNull(token);
-        return TokenStatus.OK.equals(getValidity(token));
-    }
-
-    private boolean isCorrectlySigned(SignedExpiringToken token) {
-        return signatureHandler.verify(token.getSignedContent(), token.getSignature());
-    }
-
-    private boolean isExpired(SignedExpiringToken token) {
-        return token.getExpirationDate().isBefore(zonedDateTimeProvider.get());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/AttachmentsNotFoundException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/AttachmentsNotFoundException.java
deleted file mode 100644
index 83ee287..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/AttachmentsNotFoundException.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-import java.util.List;
-
-import org.apache.james.jmap.model.BlobId;
-
-import com.google.common.collect.ImmutableList;
-
-public class AttachmentsNotFoundException extends RuntimeException {
-    
-    private List<BlobId> attachmentIds;
-
-
-    public AttachmentsNotFoundException(List<BlobId> attachmentIds) {
-        this.attachmentIds = ImmutableList.copyOf(attachmentIds);
-    }
-    
-    public List<BlobId> getAttachmentIds() {
-        return attachmentIds;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/BadRequestException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/BadRequestException.java
deleted file mode 100644
index 172c8ae..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/BadRequestException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.exceptions;
-
-public class BadRequestException extends RuntimeException {
-
-    public BadRequestException(String message) {
-        super(message);
-    }
-
-    public BadRequestException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InternalErrorException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InternalErrorException.java
deleted file mode 100644
index 7f5a548..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InternalErrorException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.exceptions;
-
-public class InternalErrorException extends RuntimeException {
-
-    public InternalErrorException(String message) {
-        super(message);
-    }
-
-    public InternalErrorException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidDraftKeywordsException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidDraftKeywordsException.java
deleted file mode 100644
index 0092035..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidDraftKeywordsException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class InvalidDraftKeywordsException extends IllegalArgumentException {
-    public InvalidDraftKeywordsException(String s) {
-        super(s);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidMailboxForCreationException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidMailboxForCreationException.java
deleted file mode 100644
index 6e8365f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidMailboxForCreationException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-import org.apache.james.mailbox.exception.MailboxException;
-
-public class InvalidMailboxForCreationException extends MailboxException {
-
-    public InvalidMailboxForCreationException(String message) {
-        super(message);
-    }
-    
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOriginMessageForMDNException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOriginMessageForMDNException.java
deleted file mode 100644
index cc4eb3b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOriginMessageForMDNException.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class InvalidOriginMessageForMDNException extends Exception {
-    private static final String MISSING_HEADER = " header is missing";
-
-    public static InvalidOriginMessageForMDNException missingHeader(String headerName) {
-        return new InvalidOriginMessageForMDNException(headerName + MISSING_HEADER);
-    }
-
-    private final String explanation;
-
-    public InvalidOriginMessageForMDNException(String explanation) {
-        this.explanation = explanation;
-    }
-
-    public String getExplanation() {
-        return explanation;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOutboxMoveException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOutboxMoveException.java
deleted file mode 100644
index 818f08d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/InvalidOutboxMoveException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class InvalidOutboxMoveException extends RuntimeException {
-    public InvalidOutboxMoveException(String message) {
-        super(message);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/JmapFieldNotSupportedException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/JmapFieldNotSupportedException.java
deleted file mode 100644
index 61ace3a..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/JmapFieldNotSupportedException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-import org.apache.commons.lang3.NotImplementedException;
-
-public class JmapFieldNotSupportedException extends NotImplementedException {
-    private final String issuer;
-    private final String field;
-
-    public JmapFieldNotSupportedException(String issuer, String field) {
-        super("not implemented");
-        this.issuer = issuer;
-        this.field = field;
-    }
-
-    public String getField() {
-        return field;
-    }
-
-    public String getIssuer() {
-        return issuer;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxHasChildException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxHasChildException.java
deleted file mode 100644
index e208aaa..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxHasChildException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class MailboxHasChildException extends Exception {
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxNotOwnedException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxNotOwnedException.java
deleted file mode 100644
index 6f2c893..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxNotOwnedException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-import org.apache.james.mailbox.exception.MailboxException;
-
-public class MailboxNotOwnedException extends MailboxException {
-
-    public MailboxNotOwnedException() {
-        super();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxParentNotFoundException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxParentNotFoundException.java
deleted file mode 100644
index 6a8005d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxParentNotFoundException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-import org.apache.james.jmap.draft.model.MailboxCreationId;
-import org.apache.james.mailbox.model.MailboxId;
-
-public class MailboxParentNotFoundException extends RuntimeException {
-
-    private final String parentId;
-
-    public MailboxParentNotFoundException(MailboxId parentId) {
-        super(String.format("The parent mailbox '%s' was not found.", parentId.serialize()));
-        this.parentId = parentId.serialize();
-    }
-
-    public MailboxParentNotFoundException(MailboxCreationId parentId) {
-        super(String.format("The parent mailbox '%s' was not found.", parentId.getCreationId()));
-        this.parentId = parentId.getCreationId();
-    }
-
-    public String getParentId() {
-        return parentId;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MalformedContinuationTokenException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MalformedContinuationTokenException.java
deleted file mode 100644
index 1046857..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MalformedContinuationTokenException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class MalformedContinuationTokenException extends Exception {
-
-    public MalformedContinuationTokenException(String s) {
-        super(s);
-    }
-
-    public MalformedContinuationTokenException(String s, Throwable throwable) {
-        super(s, throwable);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MessageNotFoundException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MessageNotFoundException.java
deleted file mode 100644
index 99c5c80..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MessageNotFoundException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class MessageNotFoundException extends Exception {
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SizeExceededException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SizeExceededException.java
deleted file mode 100644
index 1ccd76c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SizeExceededException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class SizeExceededException extends IllegalArgumentException {
-    public SizeExceededException(long actualSize, long limit) {
-        super(String.format("Attempt to create a message of %d bytes while the maximum allowed is %d", actualSize, limit));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SystemMailboxNotUpdatableException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SystemMailboxNotUpdatableException.java
deleted file mode 100644
index 2409584..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/SystemMailboxNotUpdatableException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.exceptions;
-
-public class SystemMailboxNotUpdatableException extends RuntimeException {
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FieldNamePropertyFilter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FieldNamePropertyFilter.java
deleted file mode 100644
index 649d2eb..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FieldNamePropertyFilter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import java.util.function.Predicate;
-
-import com.fasterxml.jackson.databind.ser.PropertyWriter;
-import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-
-public class FieldNamePropertyFilter extends SimpleBeanPropertyFilter {
-
-    private final Predicate<String> predicate;
-
-    public FieldNamePropertyFilter(Predicate<String> predicate) {
-        this.predicate = predicate;
-    }
-
-    @Override
-    protected boolean include(PropertyWriter writer) {
-        return predicate.test(writer.getName());
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FilterDeserializer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FilterDeserializer.java
deleted file mode 100644
index 5fa1c11..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/FilterDeserializer.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.json;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.model.Filter;
-import org.apache.james.jmap.draft.model.FilterCondition;
-import org.apache.james.jmap.draft.model.FilterOperator;
-import org.apache.james.util.streams.Iterators;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class FilterDeserializer extends StdDeserializer<Filter> {
-
-    public FilterDeserializer() {
-        super(Filter.class);
-    }
-
-    @Override
-    public Filter deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
-        ObjectMapper mapper = (ObjectMapper) p.getCodec();
-        ObjectNode obj = (ObjectNode) mapper.readTree(p);
-
-        return mapper.treeToValue(obj, detectClass(obj.fields()));
-    }
-
-    private Class<? extends Filter> detectClass(Iterator<Map.Entry<String, JsonNode>> elements) {
-        Optional<Class<? extends Filter>> maybeFilterOperator = Iterators.toStream(elements)
-                .map(Map.Entry::getKey)
-                .filter(name -> name.equals("operator"))
-                .findFirst()
-                .map(x -> FilterOperator.class);
-
-        return maybeFilterOperator.orElse(FilterCondition.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleClassesDeserializer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleClassesDeserializer.java
deleted file mode 100644
index feebcd2..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleClassesDeserializer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.json;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.google.common.base.Preconditions;
-
-public class MultipleClassesDeserializer extends StdDeserializer<Object> {
-
-    private final Map<String, Class<?>> registry = new HashMap<>();
-
-    MultipleClassesDeserializer() {
-        super(Object.class);
-    }
-
-    @Override
-    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
-        ObjectMapper mapper = (ObjectMapper) p.getCodec();
-        final JsonNode root = mapper.readTree(p);
-
-        return registry.entrySet().stream()
-                .filter(req -> ! (root.at(req.getKey()).isMissingNode()))
-                .map(x -> readValue(mapper, root, x.getValue()))
-                .findFirst()
-                .orElseThrow(() -> JsonMappingException.from(ctxt, "Can't map request to a known registered class"));
-    }
-
-    private Object readValue(ObjectMapper mapper, JsonNode root, Class<?> clazz) {
-        try {
-            return mapper.treeToValue(root, clazz);
-        } catch (JsonProcessingException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public void registerClass(String uniqueJsonPath, Class<?> clazz) {
-        Preconditions.checkArgument(! registry.containsKey(uniqueJsonPath), "Path %s has already been registered", uniqueJsonPath);
-        registry.put(uniqueJsonPath, clazz);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleObjectMapperBuilder.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleObjectMapperBuilder.java
deleted file mode 100644
index f85273d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/MultipleObjectMapperBuilder.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.json;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class MultipleObjectMapperBuilder {
-
-    private final MultipleClassesDeserializer multipleClassesDeserializer = new MultipleClassesDeserializer();
-
-    public MultipleObjectMapperBuilder registerClass(String uniqueJsonPath, Class<?> clazz) {
-        multipleClassesDeserializer.registerClass(uniqueJsonPath, clazz);
-        return this;
-    }
-
-    public ObjectMapper build() {
-        ObjectMapper mapper = new ObjectMapper();
-        SimpleModule module = new SimpleModule();
-        module.addDeserializer(Object.class, multipleClassesDeserializer);
-        mapper.registerModule(module);
-        return mapper;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeDeserializer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeDeserializer.java
deleted file mode 100644
index c43745b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeDeserializer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import java.io.IOException;
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-
-public class OptionalZonedDateTimeDeserializer extends JsonDeserializer<Optional<ZonedDateTime>> {
-
-    @Override
-    public Optional<ZonedDateTime> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
-        return Optional.ofNullable(jsonParser.getValueAsString())
-            .map(ZonedDateTime::parse);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeSerializer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeSerializer.java
deleted file mode 100644
index 84ee026..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/OptionalZonedDateTimeSerializer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import java.io.IOException;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Optional;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-public class OptionalZonedDateTimeSerializer extends JsonSerializer<Optional<ZonedDateTime>> {
-
-    @Override
-    public void serialize(Optional<ZonedDateTime> optionalZonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
-        jsonGenerator.writeString(
-            optionalZonedDateTime.map(zonedDateTime -> zonedDateTime.format(DateTimeFormatter.ISO_DATE_TIME))
-                .orElse(null));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java
deleted file mode 100644
index e6a1da0..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.util.ReactorUtils.context;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.filtering.FilteringManagement;
-import org.apache.james.jmap.draft.model.GetFilterRequest;
-import org.apache.james.jmap.draft.model.GetFilterResponse;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class GetFilterMethod implements Method {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GetFilterMethod.class);
-
-    private static final Method.Request.Name METHOD_NAME = Method.Request.name("getFilter");
-    private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("filter");
-
-    private final MetricFactory metricFactory;
-    private final FilteringManagement filteringManagement;
-
-    @Inject
-    private GetFilterMethod(MetricFactory metricFactory, FilteringManagement filteringManagement) {
-        this.metricFactory = metricFactory;
-        this.filteringManagement = filteringManagement;
-    }
-
-    @Override
-    public Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return GetFilterRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(methodCallId);
-        Preconditions.checkNotNull(mailboxSession);
-        Preconditions.checkArgument(request instanceof GetFilterRequest);
-
-        GetFilterRequest filterRequest = (GetFilterRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(methodCallId, mailboxSession, filterRequest)
-                .contextWrite(context("GET_FILTER", MDCBuilder.ofValue(MDCBuilder.ACTION, "GET_FILTER")))));
-    }
-
-    private Mono<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, GetFilterRequest request) {
-        return retrieveFilter(methodCallId, mailboxSession.getUser())
-            .onErrorResume(e -> {
-                LOGGER.warn("Failed to retrieve filter", e);
-
-                return Mono.just(unKnownError(methodCallId));
-            });
-    }
-
-    private Mono<JmapResponse> retrieveFilter(MethodCallId methodCallId, Username username) {
-        return Mono.from(filteringManagement.listRulesForUser(username))
-            .map(rules -> GetFilterResponse.builder()
-                .rules(rules.getRules())
-                .build())
-            .map(getFilterResponse -> JmapResponse.builder()
-                .methodCallId(methodCallId)
-                .response(getFilterResponse)
-                .responseName(RESPONSE_NAME)
-                .build());
-    }
-
-    private JmapResponse unKnownError(MethodCallId methodCallId) {
-        return JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(RESPONSE_NAME)
-            .response(ErrorResponse.builder()
-                .type(SetError.Type.ERROR.asString())
-                .description("Failed to retrieve filter")
-                .build())
-            .build();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
deleted file mode 100644
index 2a4bdc7..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY;
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.GetMailboxesRequest;
-import org.apache.james.jmap.draft.model.GetMailboxesResponse;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.MailboxProperty;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.utils.quotas.QuotaLoaderWithDefaultPreloaded;
-import org.apache.james.jmap.http.DefaultMailboxesProvisioner;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxMetaData;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.search.MailboxQuery;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-
-public class GetMailboxesMethod implements Method {
-
-    private static final Method.Request.Name METHOD_NAME = Method.Request.name("getMailboxes");
-    private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("mailboxes");
-    private static final Optional<Map<MailboxPath, MailboxMetaData>> NO_PRELOADED_METADATA = Optional.empty();
-    private static final String ACTION = "GET_MAILBOXES";
-
-    private final MailboxManager mailboxManager;
-    private final MailboxFactory mailboxFactory;
-    private final MetricFactory metricFactory;
-    private final QuotaRootResolver quotaRootResolver;
-    private final QuotaManager quotaManager;
-    private final DefaultMailboxesProvisioner provisioner;
-
-    @Inject
-    @VisibleForTesting
-    public GetMailboxesMethod(MailboxManager mailboxManager, QuotaRootResolver quotaRootResolver, QuotaManager quotaManager, MailboxFactory mailboxFactory, MetricFactory metricFactory, DefaultMailboxesProvisioner provisioner) {
-        this.mailboxManager = mailboxManager;
-        this.mailboxFactory = mailboxFactory;
-        this.metricFactory = metricFactory;
-        this.quotaRootResolver = quotaRootResolver;
-        this.quotaManager = quotaManager;
-        this.provisioner = provisioner;
-    }
-
-    @Override
-    public Method.Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return GetMailboxesRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkArgument(request instanceof GetMailboxesRequest);
-        GetMailboxesRequest mailboxesRequest = (GetMailboxesRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(methodCallId, mailboxSession, mailboxesRequest)
-                .contextWrite(context(ACTION, mdc(mailboxesRequest)))));
-    }
-
-    private MDCBuilder mdc(GetMailboxesRequest mailboxesRequest) {
-        return MDCBuilder.create()
-            .addToContext(MDCBuilder.ACTION, ACTION)
-            .addToContextIfPresent("accountId", mailboxesRequest.getAccountId())
-            .addToContext("mailboxIds", mailboxesRequest.getIds().toString())
-            .addToContext("properties", mailboxesRequest.getProperties().toString());
-    }
-
-    private Flux<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, GetMailboxesRequest mailboxesRequest) {
-        return provisioner.createMailboxesIfNeeded(mailboxSession)
-            .thenMany(Flux.from(getMailboxesResponse(mailboxesRequest, mailboxSession)
-                .map(response -> JmapResponse.builder().methodCallId(methodCallId)
-                    .response(response)
-                    .properties(mailboxesRequest.getProperties().map(this::ensureContainsId))
-                    .responseName(RESPONSE_NAME)
-                    .build())));
-    }
-
-    private Set<MailboxProperty> ensureContainsId(Set<MailboxProperty> input) {
-        return Sets.union(input, ImmutableSet.of(MailboxProperty.ID)).immutableCopy();
-    }
-
-    private Mono<GetMailboxesResponse> getMailboxesResponse(GetMailboxesRequest mailboxesRequest, MailboxSession mailboxSession) {
-        Optional<ImmutableList<MailboxId>> mailboxIds = mailboxesRequest.getIds();
-        return retrieveMailboxes(mailboxIds, mailboxSession)
-            .sort(Comparator.comparing(Mailbox::getSortOrder))
-            .reduce(GetMailboxesResponse.builder(), GetMailboxesResponse.Builder::add)
-            .map(GetMailboxesResponse.Builder::build);
-    }
-
-    private Flux<Mailbox> retrieveMailboxes(Optional<ImmutableList<MailboxId>> mailboxIds, MailboxSession mailboxSession) {
-        return mailboxIds
-            .map(ids -> retrieveSpecificMailboxes(mailboxSession, ids))
-            .orElseGet(Throwing.supplier(() -> retrieveAllMailboxes(mailboxSession)).sneakyThrow());
-    }
-
-
-    private Flux<Mailbox> retrieveSpecificMailboxes(MailboxSession mailboxSession, ImmutableList<MailboxId> mailboxIds) {
-        return Flux.fromIterable(mailboxIds)
-            .flatMap(mailboxId -> mailboxFactory.builder()
-                    .id(mailboxId)
-                    .session(mailboxSession)
-                    .usingPreloadedMailboxesMetadata(NO_PRELOADED_METADATA)
-                    .build(), DEFAULT_CONCURRENCY);
-    }
-
-    private Flux<Mailbox> retrieveAllMailboxes(MailboxSession mailboxSession) {
-        Mono<Map<MailboxPath, MailboxMetaData>> userMailboxesMono = getAllMailboxesMetaData(mailboxSession).collectMap(m -> m.getPath());
-        Mono<QuotaLoaderWithDefaultPreloaded> quotaLoaderMono = QuotaLoaderWithDefaultPreloaded.preLoad(quotaRootResolver, quotaManager, mailboxSession);
-
-        return userMailboxesMono.zipWith(quotaLoaderMono)
-            .publishOn(Schedulers.parallel())
-            .flatMapMany(
-                tuple -> Flux.fromIterable(tuple.getT1().values())
-                    .flatMap(mailboxMetaData -> mailboxFactory.builder()
-                        .mailboxMetadata(mailboxMetaData)
-                        .session(mailboxSession)
-                        .usingPreloadedMailboxesMetadata(Optional.of(tuple.getT1()))
-                        .quotaLoader(tuple.getT2())
-                        .build()));
-    }
-
-    private Flux<MailboxMetaData> getAllMailboxesMetaData(MailboxSession mailboxSession) {
-        return mailboxManager.search(
-            MailboxQuery.builder()
-                .matchesAllMailboxNames()
-                .build(),
-            mailboxSession);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
deleted file mode 100644
index e02206d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static jakarta.mail.Flags.Flag.DELETED;
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.time.ZonedDateTime;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Stream;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.jmap.api.projections.EmailQueryView;
-import org.apache.james.jmap.draft.model.Filter;
-import org.apache.james.jmap.draft.model.FilterCondition;
-import org.apache.james.jmap.draft.model.GetMessageListRequest;
-import org.apache.james.jmap.draft.model.GetMessageListResponse;
-import org.apache.james.jmap.draft.model.GetMessagesRequest;
-import org.apache.james.jmap.draft.utils.FilterToCriteria;
-import org.apache.james.jmap.draft.utils.SortConverter;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxId.Factory;
-import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-import org.apache.james.util.streams.Limit;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-
-public class GetMessageListMethod implements Method {
-    private static final long DEFAULT_POSITION = 0;
-    public static final String MAXIMUM_LIMIT = "maximumLimit";
-    public static final long DEFAULT_MAXIMUM_LIMIT = 256;
-
-    private static final Method.Request.Name METHOD_NAME = Method.Request.name("getMessageList");
-    private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("messageList");
-
-    private final MailboxManager mailboxManager;
-    private final long maximumLimit;
-    private final GetMessagesMethod getMessagesMethod;
-    private final Factory mailboxIdFactory;
-    private final EmailQueryView emailQueryView;
-    private final JMAPConfiguration configuration;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    private GetMessageListMethod(MailboxManager mailboxManager,
-                                 @Named(MAXIMUM_LIMIT) long maximumLimit,
-                                 GetMessagesMethod getMessagesMethod,
-                                 Factory mailboxIdFactory,
-                                 EmailQueryView emailQueryView,
-                                 JMAPConfiguration configuration,
-                                 MetricFactory metricFactory) {
-
-        this.mailboxManager = mailboxManager;
-        this.maximumLimit = maximumLimit;
-        this.getMessagesMethod = getMessagesMethod;
-        this.mailboxIdFactory = mailboxIdFactory;
-        this.emailQueryView = emailQueryView;
-        this.configuration = configuration;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Method.Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return GetMessageListRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkArgument(request instanceof GetMessageListRequest);
-
-        GetMessageListRequest messageListRequest = (GetMessageListRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(methodCallId, mailboxSession, messageListRequest)
-                .contextWrite(context("GET_MESSAGE_LIST", mdc(messageListRequest)))));
-    }
-
-    private MDCBuilder mdc(GetMessageListRequest messageListRequest) {
-        return MDCBuilder.create()
-            .addToContext(MDCBuilder.ACTION, "GET_MESSAGE_LIST")
-            .addToContextIfPresent("accountId", messageListRequest.getAccountId())
-            .addToContextIfPresent("limit", messageListRequest.getLimit()
-                .map(Number::asLong).map(l -> Long.toString(l)))
-            .addToContextIfPresent("anchor", messageListRequest.getAnchor())
-            .addToContextIfPresent("offset", messageListRequest.getAnchorOffset()
-                .map(Number::asLong).map(l -> Long.toString(l)))
-            .addToContext("properties", Joiner.on(", ")
-                .join(messageListRequest.getFetchMessageProperties()))
-            .addToContextIfPresent("position", messageListRequest.getPosition()
-                .map(Number::asLong).map(l -> Long.toString(l)))
-            .addToContextIfPresent("filters", messageListRequest.getFilter().map(Objects::toString))
-            .addToContext("sorts", Joiner.on(", ")
-                .join(messageListRequest.getSort()))
-            .addToContextIfPresent("isFetchMessage", messageListRequest.isFetchMessages().map(b -> Boolean.toString(b)))
-            .addToContextIfPresent("isCollapseThread", messageListRequest.isCollapseThreads().map(b -> Boolean.toString(b)));
-    }
-
-    private Flux<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, GetMessageListRequest messageListRequest) {
-        return getMessageListResponse(messageListRequest, mailboxSession)
-            .flatMapMany(messageListResponse -> Flux.concat(
-                Mono.just(JmapResponse.builder().methodCallId(methodCallId)
-                    .response(messageListResponse)
-                    .responseName(RESPONSE_NAME)
-                    .build()),
-                processGetMessages(messageListRequest, messageListResponse, methodCallId, mailboxSession)))
-            .onErrorResume(NotImplementedException.class, e -> Mono.just(JmapResponse.builder()
-                .methodCallId(methodCallId)
-                .responseName(RESPONSE_NAME)
-                .error(ErrorResponse.builder()
-                    .type("invalidArguments")
-                    .description(e.getMessage())
-                    .build())
-                .build()))
-            .onErrorResume(Filter.TooDeepFilterHierarchyException.class, e -> Mono.just(JmapResponse.builder()
-                .methodCallId(methodCallId)
-                .responseName(RESPONSE_NAME)
-                .error(ErrorResponse.builder()
-                    .type("invalidArguments")
-                    .description(e.getMessage())
-                    .build())
-                .build()));
-    }
-
-    private Mono<GetMessageListResponse> getMessageListResponse(GetMessageListRequest messageListRequest, MailboxSession mailboxSession) {
-        long position = messageListRequest.getPosition().map(Number::asLong).orElse(DEFAULT_POSITION);
-        long limit = position + messageListRequest.getLimit().map(Number::asLong).orElse(maximumLimit);
-
-        if (isListingContentInMailboxQuery(messageListRequest)) {
-            Filter filter = messageListRequest.getFilter().get();
-            FilterCondition condition = (FilterCondition) filter;
-            String mailboxIdAsString = condition.getInMailboxes().get().iterator().next();
-            MailboxId mailboxId = mailboxIdFactory.fromString(mailboxIdAsString);
-            Limit aLimit = Limit.from(Math.toIntExact(limit));
-
-            return Mono.from(mailboxManager.getMailboxReactive(mailboxId, mailboxSession))
-                .then(emailQueryView.listMailboxContentSortedBySentAt(mailboxId, aLimit)
-                    .skip(position)
-                    .take(limit)
-                    .reduce(GetMessageListResponse.builder(), GetMessageListResponse.Builder::messageId)
-                    .map(GetMessageListResponse.Builder::build))
-                .onErrorResume(MailboxNotFoundException.class, e -> Mono.just(GetMessageListResponse.builder().build()));
-        }
-        if (isListingContentInMailboxAfterQuery(messageListRequest)) {
-            Filter filter = messageListRequest.getFilter().get();
-            FilterCondition condition = (FilterCondition) filter;
-            String mailboxIdAsString = condition.getInMailboxes().get().iterator().next();
-            MailboxId mailboxId = mailboxIdFactory.fromString(mailboxIdAsString);
-            ZonedDateTime after = condition.getAfter().get();
-            Limit aLimit = Limit.from(Math.toIntExact(limit));
-
-            return Mono.from(mailboxManager.getMailboxReactive(mailboxId, mailboxSession))
-                .then(emailQueryView.listMailboxContentSinceAfterSortedBySentAt(mailboxId, after, aLimit)
-                    .skip(position)
-                    .take(limit)
-                    .reduce(GetMessageListResponse.builder(), GetMessageListResponse.Builder::messageId)
-                    .map(GetMessageListResponse.Builder::build))
-                .onErrorResume(MailboxNotFoundException.class, e -> Mono.just(GetMessageListResponse.builder().build()));
-        }
-        return querySearchBackend(messageListRequest, position, limit, mailboxSession);
-    }
-
-    private boolean isListingContentInMailboxQuery(GetMessageListRequest messageListRequest) {
-        return configuration.isEmailQueryViewEnabled()
-            && messageListRequest.getFilter().map(Filter::inMailboxFilterOnly).orElse(false)
-            && messageListRequest.getSort().equals(ImmutableList.of("date desc"));
-    }
-
-    private boolean isListingContentInMailboxAfterQuery(GetMessageListRequest messageListRequest) {
-        return configuration.isEmailQueryViewEnabled()
-            && messageListRequest.getFilter().map(Filter::inMailboxAndAfterFiltersOnly).orElse(false)
-            && messageListRequest.getSort().equals(ImmutableList.of("date desc"));
-    }
-
-    private Mono<GetMessageListResponse> querySearchBackend(GetMessageListRequest messageListRequest, long position, long limit, MailboxSession mailboxSession) {
-        Mono<MultimailboxesSearchQuery> searchQuery = Mono.fromCallable(() -> convertToSearchQuery(messageListRequest))
-            .subscribeOn(Schedulers.parallel());
-
-        return searchQuery
-            .flatMapMany(Throwing.function(query ->
-                mailboxManager.search(query.addCriterion(SearchQuery.flagIsUnSet(DELETED)), mailboxSession, limit)))
-            .skip(position)
-            .reduce(GetMessageListResponse.builder(), GetMessageListResponse.Builder::messageId)
-            .map(GetMessageListResponse.Builder::build);
-    }
-
-    private MultimailboxesSearchQuery convertToSearchQuery(GetMessageListRequest messageListRequest) {
-        if (messageListRequest.getFilter().map(this::containsNestedMailboxFilters).orElse(false)) {
-            throw new NotImplementedException("'inMailboxes' and 'notInMailboxes' wrapped within Filter Operators are not " +
-                "implemented. Review your search request.");
-        }
-
-        SearchQuery.Builder searchQueryBuilder = SearchQuery.builder();
-
-        messageListRequest.getFilter()
-            .map(filter -> new FilterToCriteria().convert(filter).collect(ImmutableList.toImmutableList()))
-            .ifPresent(searchQueryBuilder::andCriteria);
-        Set<MailboxId> inMailboxes = buildFilterMailboxesSet(messageListRequest.getFilter(), FilterCondition::getInMailboxes);
-        Set<MailboxId> notInMailboxes = buildFilterMailboxesSet(messageListRequest.getFilter(), FilterCondition::getNotInMailboxes);
-        List<SearchQuery.Sort> sorts = SortConverter.convertToSorts(messageListRequest.getSort());
-        if (!sorts.isEmpty()) {
-            searchQueryBuilder.sorts(sorts);
-        }
-        return MultimailboxesSearchQuery
-            .from(searchQueryBuilder.build())
-            .inMailboxes(inMailboxes)
-            .notInMailboxes(notInMailboxes)
-            .build();
-    }
-
-    private boolean containsNestedMailboxFilters(Filter filter) {
-        if (filter instanceof FilterCondition) {
-            // The condition is not nested
-            return false;
-        }
-        return containsMailboxFilters(filter);
-    }
-
-    private boolean containsMailboxFilters(Filter filter) {
-        return filter.breadthFirstVisit()
-            .stream()
-            .anyMatch(this::hasMailboxClause);
-    }
-
-    private boolean hasMailboxClause(FilterCondition condition) {
-        return condition.getInMailboxes().isPresent() || condition.getInMailboxes().isPresent();
-    }
-
-    private Set<MailboxId> buildFilterMailboxesSet(Optional<Filter> maybeFilter, Function<FilterCondition, Optional<List<String>>> mailboxListExtractor) {
-        return filterToFilterCondition(maybeFilter)
-            .flatMap(condition -> mailboxListExtractor.apply(condition).stream())
-            .flatMap(List::stream)
-            .map(mailboxIdFactory::fromString)
-            .collect(ImmutableSet.toImmutableSet());
-    }
-    
-    private Stream<FilterCondition> filterToFilterCondition(Optional<Filter> maybeCondition) {
-        return maybeCondition.stream()
-            .flatMap(c -> {
-                if (c instanceof FilterCondition) {
-                    return Stream.of((FilterCondition)c);
-                }
-                return Stream.of();
-            });
-    }
-
-    private Flux<JmapResponse> processGetMessages(GetMessageListRequest messageListRequest, GetMessageListResponse messageListResponse, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        if (shouldChainToGetMessages(messageListRequest)) {
-            GetMessagesRequest getMessagesRequest = GetMessagesRequest.builder()
-                .ids(messageListResponse.getMessageIds())
-                .properties(messageListRequest.getFetchMessageProperties())
-                .build();
-            return getMessagesMethod.process(getMessagesRequest, methodCallId, mailboxSession);
-        }
-        return Flux.empty();
-    }
-
-    private boolean shouldChainToGetMessages(GetMessageListRequest messageListRequest) {
-        return messageListRequest.isFetchMessages().orElse(false)
-            && !messageListRequest.isFetchThreads().orElse(false);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java
deleted file mode 100644
index d912b42..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.draft.json.FieldNamePropertyFilter;
-import org.apache.james.jmap.draft.model.GetMessagesRequest;
-import org.apache.james.jmap.draft.model.GetMessagesResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.model.Property;
-import org.apache.james.jmap.model.message.view.MessageView;
-import org.apache.james.jmap.model.message.view.MessageViewFactory;
-import org.apache.james.jmap.model.message.view.MetaMessageViewFactory;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-
-import com.fasterxml.jackson.databind.ser.PropertyFilter;
-import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class GetMessagesMethod implements Method {
-
-    public static final String HEADERS_FILTER = "headersFilter";
-    private static final String ISSUER = "GetMessagesMethod";
-    private static final Method.Request.Name METHOD_NAME = Method.Request.name("getMessages");
-    private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("messages");
-    private final MetaMessageViewFactory messageViewFactory;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting GetMessagesMethod(MetaMessageViewFactory messageViewFactory, MetricFactory metricFactory) {
-        this.messageViewFactory = messageViewFactory;
-        this.metricFactory = metricFactory;
-    }
-    
-    @Override
-    public Method.Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-    
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return GetMessagesRequest.class;
-    }
-    
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(mailboxSession);
-        Preconditions.checkArgument(request instanceof GetMessagesRequest);
-
-        GetMessagesRequest getMessagesRequest = (GetMessagesRequest) request;
-        MessageProperties outputProperties = getMessagesRequest.getProperties().toOutputProperties();
-        Optional<Pair<? extends Set<? extends Property>, SimpleFilterProvider>> integerSimpleFilterProviderPair = buildOptionalHeadersFilteringFilterProvider(outputProperties);
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            Flux.from(getMessagesResponse(mailboxSession, getMessagesRequest)
-                .map(response -> JmapResponse.builder().methodCallId(methodCallId)
-                    .response(response)
-                    .responseName(RESPONSE_NAME)
-                    .properties(outputProperties.getOptionalMessageProperties())
-                    .filterProvider(integerSimpleFilterProviderPair)
-                    .build()))
-            .contextWrite(context("GET_MESSAGES", mdc(getMessagesRequest)))));
-    }
-
-    private MDCBuilder mdc(GetMessagesRequest getMessagesRequest) {
-        return MDCBuilder.create()
-            .addToContext(MDCBuilder.ACTION, "GET_MESSAGES")
-            .addToContextIfPresent("accountId", getMessagesRequest.getAccountId())
-            .addToContext("ids", getMessagesRequest.getIds()
-                .stream()
-                .map(MessageId::serialize)
-                .collect(Collectors.joining(", ")))
-            .addToContext("properties", getMessagesRequest.getProperties().asFieldList()
-                .collect(Collectors.joining(", ")));
-    }
-
-    private Optional<Pair<? extends Set<? extends Property>, SimpleFilterProvider>> buildOptionalHeadersFilteringFilterProvider(MessageProperties properties) {
-        return properties.getOptionalHeadersProperties()
-            .map(headerProperties -> Pair.of(headerProperties, new SimpleFilterProvider()
-                .addFilter(HEADERS_FILTER, buildHeadersPropertyFilter(headerProperties))));
-    }
-    
-    private PropertyFilter buildHeadersPropertyFilter(ImmutableSet<HeaderProperty> headerProperties) {
-        return new FieldNamePropertyFilter((fieldName) -> headerProperties.contains(HeaderProperty.fromFieldName(fieldName)));
-    }
-
-    private Mono<GetMessagesResponse> getMessagesResponse(MailboxSession mailboxSession, GetMessagesRequest getMessagesRequest) {
-        getMessagesRequest.getAccountId().ifPresent(input -> notImplemented("accountId"));
-
-        MessageProperties.ReadProfile readProfile = getMessagesRequest.getProperties().computeReadLevel();
-        MessageViewFactory<? extends MessageView> factory = messageViewFactory.getFactory(readProfile);
-        Mono<? extends Set<? extends MessageView>> messageViewsMono = factory.fromMessageIds(getMessagesRequest.getIds(), mailboxSession)
-            .collect(ImmutableSet.toImmutableSet());
-
-        return messageViewsMono.map(messageViews ->
-            GetMessagesResponse.builder()
-                .messages(messageViews)
-                .expectedMessageIds(getMessagesRequest.getIds())
-                .build());
-    }
-
-    private static void notImplemented(String field) {
-        throw new JmapFieldNotSupportedException(ISSUER, field);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java
deleted file mode 100644
index b2a3e9f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.jmap.utils.AccountIdUtil.toVacationAccountId;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.api.model.AccountId;
-import org.apache.james.jmap.draft.model.GetVacationRequest;
-import org.apache.james.jmap.draft.model.GetVacationResponse;
-import org.apache.james.jmap.draft.model.VacationResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.date.ZonedDateTimeProvider;
-import org.apache.james.vacation.api.Vacation;
-import org.apache.james.vacation.api.VacationService;
-
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class GetVacationResponseMethod implements Method {
-
-    public static final Request.Name METHOD_NAME = Request.name("getVacationResponse");
-    public static final Response.Name RESPONSE_NAME = Method.Response.name("vacationResponse");
-
-    private final VacationService vacationService;
-    private final ZonedDateTimeProvider zonedDateTimeProvider;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    public GetVacationResponseMethod(VacationService vacationService, ZonedDateTimeProvider zonedDateTimeProvider, MetricFactory metricFactory) {
-        this.vacationService = vacationService;
-        this.zonedDateTimeProvider = zonedDateTimeProvider;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return GetVacationRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(methodCallId);
-        Preconditions.checkNotNull(mailboxSession);
-        Preconditions.checkArgument(request instanceof GetVacationRequest);
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(mailboxSession)
-                .map(response -> JmapResponse.builder()
-                    .methodCallId(methodCallId)
-                    .responseName(RESPONSE_NAME)
-                    .response(response)
-                    .build())
-                .flux()))
-            .contextWrite(jmapAction("VACATION"));
-    }
-
-    private Mono<GetVacationResponse> process(MailboxSession mailboxSession) {
-        return vacationService.retrieveVacation(toVacationAccountId(AccountId.fromUsername(mailboxSession.getUser())))
-            .map(vacation -> asVacationResponse(mailboxSession, vacation));
-    }
-
-    private GetVacationResponse asVacationResponse(MailboxSession mailboxSession, Vacation vacation) {
-        return GetVacationResponse.builder()
-            .accountId(mailboxSession.getUser().asString())
-            .vacationResponse(VacationResponse.builder()
-                .fromVacation(vacation)
-                .activated(vacation.isActiveAtDate(zonedDateTimeProvider.get()))
-                .build())
-            .build();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParser.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParser.java
deleted file mode 100644
index 9be20e3..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParser.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.IOException;
-
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.methods.JmapRequest;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-
-public interface JmapRequestParser {
-
-    <T extends JmapRequest> T extractJmapRequest(InvocationRequest request, Class<T> requestClass)
-            throws IOException, JsonParseException, JsonMappingException;
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParserImpl.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParserImpl.java
deleted file mode 100644
index 47608d2..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/JmapRequestParserImpl.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.IOException;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.methods.JmapRequest;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Preconditions;
-
-public class JmapRequestParserImpl implements JmapRequestParser {
-
-    private final ObjectMapper objectMapper;
-
-    @Inject
-    public JmapRequestParserImpl(ObjectMapperFactory objectMapperFactory) {
-        this.objectMapper = objectMapperFactory.forParsing();
-    }
-
-    @Override
-    public <T extends JmapRequest> T extractJmapRequest(InvocationRequest request, Class<T> requestClass)
-            throws IOException, JsonParseException, JsonMappingException {
-        Preconditions.checkNotNull(requestClass, "requestClass should not be null");
-
-        return objectMapper.treeToValue(request.getParameters(), requestClass);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java
deleted file mode 100644
index c7acdf3..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TimeZone;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessage.DraftEmailer;
-import org.apache.james.jmap.model.Attachment;
-import org.apache.james.jmap.model.message.view.MessageViewFactory;
-import org.apache.james.mime4j.codec.DecodeMonitor;
-import org.apache.james.mime4j.codec.EncoderUtil;
-import org.apache.james.mime4j.codec.EncoderUtil.Usage;
-import org.apache.james.mime4j.dom.FieldParser;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.Multipart;
-import org.apache.james.mime4j.dom.TextBody;
-import org.apache.james.mime4j.dom.address.Mailbox;
-import org.apache.james.mime4j.dom.field.ContentDispositionField;
-import org.apache.james.mime4j.dom.field.ContentTypeField;
-import org.apache.james.mime4j.dom.field.FieldName;
-import org.apache.james.mime4j.dom.field.UnstructuredField;
-import org.apache.james.mime4j.field.ContentIdFieldImpl;
-import org.apache.james.mime4j.field.Fields;
-import org.apache.james.mime4j.field.LenientFieldParser;
-import org.apache.james.mime4j.field.UnstructuredFieldImpl;
-import org.apache.james.mime4j.message.BasicBodyFactory;
-import org.apache.james.mime4j.message.BodyPart;
-import org.apache.james.mime4j.message.BodyPartBuilder;
-import org.apache.james.mime4j.message.DefaultMessageWriter;
-import org.apache.james.mime4j.message.MultipartBuilder;
-import org.apache.james.mime4j.stream.NameValuePair;
-import org.apache.james.mime4j.stream.RawField;
-import org.apache.james.mime4j.util.MimeUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
-import com.google.common.net.MediaType;
-
-public class MIMEMessageConverter {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(MIMEMessageConverter.class);
-
-    private static final String PLAIN_TEXT_MEDIA_TYPE = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
-    private static final String HTML_MEDIA_TYPE = MediaType.HTML_UTF_8.withoutParameters().toString();
-    private static final NameValuePair UTF_8_CHARSET = new NameValuePair("charset", StandardCharsets.UTF_8.name());
-    private static final String ALTERNATIVE_SUB_TYPE = "alternative";
-    private static final String MIXED_SUB_TYPE = "mixed";
-    private static final String RELATED_SUB_TYPE = "related";
-    private static final String QUOTED_PRINTABLE = "quoted-printable";
-    private static final String BASE64 = "base64";
-    private static final String IN_REPLY_TO_HEADER = "In-Reply-To";
-    private static final List<String> COMPUTED_HEADERS = ImmutableList.of(
-            FieldName.FROM,
-            FieldName.SENDER,
-            FieldName.REPLY_TO,
-            FieldName.TO,
-            FieldName.CC,
-            FieldName.BCC,
-            FieldName.SUBJECT,
-            FieldName.MESSAGE_ID,
-            FieldName.DATE,
-            FieldName.CONTENT_TYPE,
-            FieldName.MIME_VERSION,
-            FieldName.CONTENT_TRANSFER_ENCODING);
-    private static final List<String> LOWERCASED_COMPUTED_HEADERS = COMPUTED_HEADERS.stream()
-            .map(s -> s.toLowerCase(Locale.ENGLISH))
-            .collect(ImmutableList.toImmutableList());
-    private static final LenientFieldParser FIELD_PARSER = new LenientFieldParser();
-
-    private final BasicBodyFactory bodyFactory;
-
-    @Inject
-    public MIMEMessageConverter() {
-        this.bodyFactory = new BasicBodyFactory();
-    }
-
-    public byte[] convert(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<Attachment.WithBlob> messageAttachments) {
-        Message message = convertToMime(creationMessageEntry, messageAttachments);
-        byte[] result = asBytes(message);
-        message.dispose();
-        return result;
-    }
-
-    public byte[] asBytes(Message message) {
-        try {
-            return DefaultMessageWriter.asBytes(message);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @VisibleForTesting Message convertToMime(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<Attachment.WithBlob> messageAttachments) {
-        if (creationMessageEntry == null || creationMessageEntry.getValue() == null) {
-            throw new IllegalArgumentException("creationMessageEntry is either null or has null message");
-        }
-
-        Message.Builder messageBuilder = Message.Builder.of()
-            .use(FIELD_PARSER);
-        if (isMultipart(creationMessageEntry.getValue(), messageAttachments)) {
-            messageBuilder.setBody(createMultipart(creationMessageEntry.getValue(), messageAttachments));
-        } else {
-            messageBuilder.setBody(createTextBody(creationMessageEntry.getValue()))
-                .setContentTransferEncoding(QUOTED_PRINTABLE);
-        }
-        buildMimeHeaders(messageBuilder, creationMessageEntry.getValue(), messageAttachments);
-        return messageBuilder.build();
-    }
-
-    private void buildMimeHeaders(Message.Builder messageBuilder, CreationMessage newMessage, ImmutableList<Attachment.WithBlob> messageAttachments) {
-        Optional<Mailbox> fromAddress = newMessage.getFrom().filter(DraftEmailer::hasValidEmail).map(this::convertEmailToMimeHeader);
-        fromAddress.ifPresent(messageBuilder::setFrom);
-        fromAddress.ifPresent(messageBuilder::setSender);
-
-        messageBuilder.setReplyTo(newMessage.getReplyTo().stream()
-                .map(this::convertEmailToMimeHeader)
-                .collect(Collectors.toList()));
-        messageBuilder.setTo(newMessage.getTo().stream()
-                .filter(DraftEmailer::hasValidEmail)
-                .map(this::convertEmailToMimeHeader)
-                .collect(Collectors.toList()));
-        messageBuilder.setCc(newMessage.getCc().stream()
-                .filter(DraftEmailer::hasValidEmail)
-                .map(this::convertEmailToMimeHeader)
-                .collect(Collectors.toList()));
-        messageBuilder.setBcc(newMessage.getBcc().stream()
-                .filter(DraftEmailer::hasValidEmail)
-                .map(this::convertEmailToMimeHeader)
-                .collect(Collectors.toList()));
-        messageBuilder.setSubject(newMessage.getSubject());
-        messageBuilder.setMessageId(generateUniqueMessageId(fromAddress));
-
-        // note that date conversion probably lose milliseconds!
-        messageBuilder.setDate(Date.from(newMessage.getDate().toInstant()), TimeZone.getTimeZone(newMessage.getDate().getZone()));
-        newMessage.getInReplyToMessageId()
-            .ifPresent(id -> addHeader(messageBuilder, IN_REPLY_TO_HEADER, id));
-        if (!isMultipart(newMessage, messageAttachments)) {
-            newMessage.getHtmlBody().ifPresent(x -> messageBuilder.setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET));
-        }
-        newMessage.getHeaders().entrySet().stream()
-            .filter(header -> ! header.getKey().trim().isEmpty())
-            .filter(header -> ! LOWERCASED_COMPUTED_HEADERS.contains(header.getKey().toLowerCase(Locale.ENGLISH)))
-            .forEach(header -> addMultivaluedHeader(messageBuilder, header.getKey(), header.getValue()));
-    }
-
-    private String generateUniqueMessageId(Optional<Mailbox> fromAddress) {
-        String noDomain = null;
-        return MimeUtil.createUniqueMessageId(fromAddress
-            .map(Mailbox::getDomain)
-            .orElse(noDomain));
-    }
-
-    private void addMultivaluedHeader(Message.Builder messageBuilder, String fieldName, String multipleValues) {
-        Splitter.on(MessageViewFactory.JMAP_MULTIVALUED_FIELD_DELIMITER).split(multipleValues)
-            .forEach(value -> addHeader(messageBuilder, fieldName, value));
-    }
-
-    private void addHeader(Message.Builder messageBuilder, String fieldName, String value) {
-        FieldParser<UnstructuredField> parser = UnstructuredFieldImpl.PARSER;
-        RawField rawField = new RawField(fieldName, value);
-        messageBuilder.addField(parser.parse(rawField, DecodeMonitor.SILENT));
-    }
-
-    private boolean isMultipart(CreationMessage newMessage, ImmutableList<Attachment.WithBlob> messageAttachments) {
-        return (newMessage.getTextBody().isPresent() && newMessage.getHtmlBody().isPresent())
-                || hasAttachment(messageAttachments);
-    }
-
-    private boolean hasAttachment(ImmutableList<Attachment.WithBlob> messageAttachments) {
-        return !messageAttachments.isEmpty();
-    }
-
-    private TextBody createTextBody(CreationMessage newMessage) {
-        String body = newMessage.getHtmlBody()
-                        .orElse(newMessage.getTextBody()
-                                .orElse(""));
-        return bodyFactory.textBody(body, StandardCharsets.UTF_8);
-    }
-
-    private Multipart createMultipart(CreationMessage newMessage, ImmutableList<Attachment.WithBlob> messageAttachments) {
-        try {
-            if (hasAttachment(messageAttachments)) {
-                return createMultipartWithAttachments(newMessage, messageAttachments);
-            } else {
-                return createMultipartAlternativeBody(newMessage);
-            }
-        } catch (IOException e) {
-            LOGGER.error("Error while creating textBody \n{}\n or htmlBody \n{}", newMessage.getTextBody().get(), newMessage.getHtmlBody().get(), e);
-            throw new RuntimeException(e);
-        }
-    }
-
-    private Multipart createMultipartWithAttachments(CreationMessage newMessage, ImmutableList<Attachment.WithBlob> messageAttachments) throws IOException {
-        MultipartBuilder mixedMultipartBuilder = MultipartBuilder.create(MIXED_SUB_TYPE);
-        List<Attachment.WithBlob> inlineAttachments = messageAttachments.stream()
-            .filter(attachment -> attachment.getAttachment().isIsInline())
-            .collect(ImmutableList.toImmutableList());
-        List<Attachment.WithBlob> besideAttachments = messageAttachments.stream()
-            .filter(Predicate.not(attachment -> attachment.getAttachment().isIsInline()))
-            .collect(ImmutableList.toImmutableList());
-
-        if (inlineAttachments.size() > 0) {
-            mixedMultipartBuilder.addBodyPart(relatedInnerMessage(newMessage, inlineAttachments));
-        } else {
-            addBody(newMessage, mixedMultipartBuilder);
-        }
-
-        addAttachments(besideAttachments, mixedMultipartBuilder);
-
-        return mixedMultipartBuilder.build();
-    }
-
-    private Message relatedInnerMessage(CreationMessage newMessage, List<Attachment.WithBlob> inlines) throws IOException {
-        MultipartBuilder relatedMultipart = MultipartBuilder.create(RELATED_SUB_TYPE);
-        addBody(newMessage, relatedMultipart);
-
-        return Message.Builder.of()
-            .setBody(addAttachments(inlines, relatedMultipart)
-                .build())
-            .build();
-    }
-
-    private MultipartBuilder addAttachments(List<Attachment.WithBlob> messageAttachments,
-                                            MultipartBuilder multipartBuilder) {
-        messageAttachments.forEach(addAttachment(multipartBuilder));
-
-        return multipartBuilder;
-    }
-    
-    private void addBody(CreationMessage newMessage, MultipartBuilder builder) throws IOException {
-        if (newMessage.getHtmlBody().isPresent() && newMessage.getTextBody().isPresent()) {
-            Multipart body = createMultipartAlternativeBody(newMessage);
-            builder.addBodyPart(BodyPartBuilder.create().setBody(body));
-        } else {
-            addText(builder, newMessage.getTextBody());
-            addHtml(builder, newMessage.getHtmlBody());
-        }
-    }
-
-    private Multipart createMultipartAlternativeBody(CreationMessage newMessage) throws IOException {
-        MultipartBuilder bodyBuilder = MultipartBuilder.create(ALTERNATIVE_SUB_TYPE);
-        addText(bodyBuilder, newMessage.getTextBody());
-        addHtml(bodyBuilder, newMessage.getHtmlBody());
-        return bodyBuilder.build();
-    }
-
-    private void addText(MultipartBuilder builder, Optional<String> textBody) throws IOException {
-        if (textBody.isPresent()) {
-            builder.addBodyPart(BodyPartBuilder.create()
-                .use(bodyFactory)
-                .setBody(textBody.get(), StandardCharsets.UTF_8)
-                .setContentType(PLAIN_TEXT_MEDIA_TYPE, UTF_8_CHARSET)
-                .setContentTransferEncoding(QUOTED_PRINTABLE));
-        }
-    }
-
-    private void addHtml(MultipartBuilder builder, Optional<String> htmlBody) throws IOException {
-        if (htmlBody.isPresent()) {
-            builder.addBodyPart(BodyPartBuilder.create()
-                .use(bodyFactory)
-                .setBody(htmlBody.get(), StandardCharsets.UTF_8)
-                .setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET)
-                .setContentTransferEncoding(QUOTED_PRINTABLE));
-        }
-    }
-
-    private Consumer<Attachment.WithBlob> addAttachment(MultipartBuilder builder) {
-        return att -> { 
-            try {
-                builder.addBodyPart(attachmentBodyPart(att));
-            } catch (IOException e) {
-                LOGGER.error("Error while creating attachment", e);
-                throw new RuntimeException(e);
-            }
-        };
-    }
-
-    private BodyPart attachmentBodyPart(Attachment.WithBlob att) throws IOException {
-        try (InputStream attachmentStream = att.getBlob().getStream()) {
-            BodyPartBuilder builder = BodyPartBuilder.create()
-                .use(bodyFactory)
-                .setBody(new BasicBodyFactory().binaryBody(ByteStreams.toByteArray(attachmentStream)))
-                .setField(contentTypeField(att.getAttachment()))
-                .setField(contentDispositionField(att.getAttachment().isIsInline()))
-                .setContentTransferEncoding(BASE64);
-            contentId(builder, att);
-            return builder.build();
-        }
-    }
-
-    private void contentId(BodyPartBuilder builder, Attachment.WithBlob att) {
-        if (att.getAttachment().getCid().isPresent()) {
-            builder.setField(ContentIdFieldImpl.PARSER.parse(new RawField("Content-ID", att.getAttachment().getCid().get()), DecodeMonitor.SILENT));
-        }
-    }
-
-    @VisibleForTesting
-    ContentTypeField contentTypeField(Attachment attachment) {
-        final ContentTypeField typeAsField = attachment.getType().asMime4J();
-        if (attachment.getName().isPresent()) {
-            return Fields.contentType(typeAsField.getMimeType(),
-                ImmutableMap.<String, String>builder()
-                    .putAll(parametersWithoutName(typeAsField))
-                    .put("name", encode(attachment.getName().get()))
-                    .build());
-        }
-        return typeAsField;
-    }
-
-    private ImmutableMap<String, String> parametersWithoutName(ContentTypeField typeAsField) {
-        return typeAsField.getParameters()
-            .entrySet()
-            .stream()
-            .filter(entry -> !entry.getKey().equals("name"))
-            .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
-    }
-
-    private String encode(String name) {
-        return EncoderUtil.encodeEncodedWord(name, Usage.TEXT_TOKEN);
-    }
-
-    private ContentDispositionField contentDispositionField(boolean isInline) {
-        if (isInline) {
-            return Fields.contentDisposition("inline");
-        }
-        return Fields.contentDisposition("attachment");
-    }
-
-    private Mailbox convertEmailToMimeHeader(DraftEmailer address) {
-        if (!address.hasValidEmail()) {
-            throw new IllegalArgumentException("address");
-        }
-        CreationMessage.EmailUserAndDomain emailUserAndDomain = address.getEmailUserAndDomain();
-        return new Mailbox(address.getName().orElse(null), null, emailUserAndDomain.getUser().orElse(null), emailUserAndDomain.getDomain().orElse(null));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxInvalidMessageCreationException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxInvalidMessageCreationException.java
deleted file mode 100644
index c6d572b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxInvalidMessageCreationException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import org.apache.james.mailbox.exception.MailboxException;
-
-public class MailboxInvalidMessageCreationException extends MailboxException {
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java
deleted file mode 100644
index 8c3f0f5..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.util.Optional;
-
-import org.apache.james.core.Username;
-import org.apache.james.mailbox.exception.MailboxException;
-
-public class MailboxSendingNotAllowedException extends MailboxException {
-
-    private final Username connectedUser;
-    private final Optional<Username> fromField;
-
-    public MailboxSendingNotAllowedException(Username connectedUser, Optional<Username> fromField) {
-        super();
-        this.connectedUser = connectedUser;
-        this.fromField = fromField;
-    }
-
-    public Optional<Username> getFromField() {
-        return fromField;
-    }
-
-    public Username getConnectedUser() {
-        return connectedUser;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageAppender.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageAppender.java
deleted file mode 100644
index 978a218..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageAppender.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-import jakarta.mail.Flags;
-import jakarta.mail.util.SharedByteArrayInputStream;
-
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.jmap.draft.exceptions.AttachmentsNotFoundException;
-import org.apache.james.jmap.draft.exceptions.SizeExceededException;
-import org.apache.james.jmap.draft.methods.ValueWithId.CreationMessageEntry;
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.model.Attachment;
-import org.apache.james.jmap.model.Blob;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory.MetaDataWithContent;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageManager.AppendResult;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.ByteContent;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.util.ReactorUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class MessageAppender {
-    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAppender.class);
-
-    private static class NewMessage {
-        private final byte[] messageContent;
-        private final Message message;
-        private final Date date;
-
-        private NewMessage(byte[] messageContent, Message message, Date date) {
-            this.messageContent = messageContent;
-            this.message = message;
-            this.date = date;
-        }
-    }
-
-    private final MailboxManager mailboxManager;
-    private final MessageIdManager messageIdManager;
-    private final MIMEMessageConverter mimeMessageConverter;
-    private final BlobManager blobManager;
-    private final JMAPConfiguration configuration;
-
-    @Inject
-    public MessageAppender(MailboxManager mailboxManager, MessageIdManager messageIdManager, MIMEMessageConverter mimeMessageConverter, BlobManager blobManager, JMAPConfiguration configuration) {
-        this.mailboxManager = mailboxManager;
-        this.messageIdManager = messageIdManager;
-        this.mimeMessageConverter = mimeMessageConverter;
-        this.blobManager = blobManager;
-        this.configuration = configuration;
-    }
-
-    public Mono<MetaDataWithContent> appendMessageInMailboxes(CreationMessageEntry createdEntry, List<MailboxId> targetMailboxes, MailboxSession session) {
-        return Mono.fromCallable(() -> {
-                Preconditions.checkArgument(!targetMailboxes.isEmpty());
-                ImmutableList<Attachment.WithBlob> attachmentsWithBlobs = getMessageAttachments(session, createdEntry.getValue().getAttachments());
-                Message message = mimeMessageConverter.convertToMime(createdEntry, attachmentsWithBlobs);
-
-                byte[] messageContent = mimeMessageConverter.asBytes(message);
-
-                if (maximumSizeExceeded(messageContent)) {
-                    throw new SizeExceededException(messageContent.length, configuration.getMaximumSendSize().get());
-                }
-
-                Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
-
-                return new NewMessage(messageContent, message, internalDate);
-            }).subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER)
-            .flatMap(newMessage -> Mono.from(mailboxManager.getMailboxReactive(targetMailboxes.get(0), session))
-                .flatMap(mailbox -> Mono.from(mailbox.appendMessageReactive(
-                    MessageManager.AppendCommand.builder()
-                        .withInternalDate(newMessage.date)
-                        .withFlags(getFlags(createdEntry.getValue()))
-                        .notRecent()
-                        .withParsedMessage(newMessage.message)
-                        .build(new ByteContent(newMessage.messageContent)),
-                    session))
-                    .flatMap(appendResult -> {
-                        ComposedMessageId ids = appendResult.getId();
-                        if (targetMailboxes.size() > 1) {
-                            return Mono.from(messageIdManager.setInMailboxesReactive(ids.getMessageId(), targetMailboxes, session))
-                                .thenReturn(appendResult);
-                        }
-                        return Mono.just(appendResult);
-                    })
-                    .doFinally(any -> newMessage.message.dispose())
-                    .map(appendResult -> MetaDataWithContent.builder()
-                        .uid(appendResult.getId().getUid())
-                        .keywords(createdEntry.getValue().getKeywords())
-                        .internalDate(newMessage.date.toInstant())
-                        .sharedContent(new SharedByteArrayInputStream(newMessage.messageContent))
-                        .size(newMessage.messageContent.length)
-                        .attachments(appendResult.getMessageAttachments())
-                        .mailboxId(mailbox.getId())
-                        .message(newMessage.message)
-                        .messageId(appendResult.getId().getMessageId())
-                        .build())));
-    }
-
-    private Boolean maximumSizeExceeded(byte[] messageContent) {
-        return configuration.getMaximumSendSize().map(limit -> messageContent.length > limit).orElse(false);
-    }
-
-    public MetaDataWithContent appendMessageInMailbox(org.apache.james.mime4j.dom.Message message,
-                                                      MessageManager messageManager,
-                                                      Flags flags,
-                                                      MailboxSession session) throws MailboxException {
-
-
-        byte[] messageContent = mimeMessageConverter.asBytes(message);
-        Date internalDate = new Date();
-
-        AppendResult appendResult = messageManager.appendMessage(MessageManager.AppendCommand.builder()
-            .withFlags(flags)
-            .build(new ByteContent(messageContent)), session);
-        ComposedMessageId ids = appendResult.getId();
-
-        return MetaDataWithContent.builder()
-            .uid(ids.getUid())
-            .keywords(Keywords.lenientFactory().fromFlags(flags))
-            .internalDate(internalDate.toInstant())
-            .sharedContent(new SharedByteArrayInputStream(messageContent))
-            .size(messageContent.length)
-            .attachments(appendResult.getMessageAttachments())
-            .mailboxId(messageManager.getId())
-            .message(message)
-            .messageId(ids.getMessageId())
-            .build();
-    }
-
-    private Flags getFlags(CreationMessage message) {
-        return message.getKeywords().asFlags();
-    }
-
-    private ImmutableList<Attachment.WithBlob> getMessageAttachments(MailboxSession session, ImmutableList<Attachment> attachments) {
-        ImmutableMap<BlobId, Blob> blobs = Flux.from(blobManager.retrieve(attachments.stream()
-                .map(Attachment::getBlobId)
-                .collect(ImmutableList.toImmutableList()),
-            session))
-            .collect(ImmutableMap.toImmutableMap(Blob::getBlobId, Function.identity()))
-            .block();
-
-        ImmutableList<Attachment.WithBlob> result = attachments
-            .stream()
-            .flatMap(attachment -> Optional.ofNullable(blobs.get(attachment.getBlobId()))
-                .map(blob -> new Attachment.WithBlob(attachment, blob))
-                .stream())
-            .collect(ImmutableList.toImmutableList());
-
-        if (result.size() != attachments.size()) {
-            Sets.SetView<BlobId> notFound = Sets.difference(attachments.stream().map(Attachment::getBlobId).collect(Collectors.toSet()),
-                result.stream().map(att -> att.getAttachment().getBlobId()).collect(Collectors.toSet()));
-            throw new AttachmentsNotFoundException(ImmutableList.copyOf(notFound));
-        }
-        return result;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
deleted file mode 100644
index e443735..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.InputStream;
-
-import jakarta.inject.Inject;
-import jakarta.mail.MessagingException;
-
-import org.apache.james.jmap.draft.send.MailSpool;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory.MetaDataWithContent;
-import org.apache.james.jmap.send.MailMetadata;
-import org.apache.james.lifecycle.api.LifecycleUtil;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.server.core.Envelope;
-import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageSource;
-import org.apache.mailet.Mail;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-
-public class MessageSender {
-    public static class MessageMimeMessageSource implements MimeMessageSource {
-        private final String id;
-        private final MetaDataWithContent message;
-
-        public MessageMimeMessageSource(String id, MetaDataWithContent message) {
-            this.id = id;
-            this.message = message;
-        }
-
-        @Override
-        public String getSourceId() {
-            return id;
-        }
-
-        @Override
-        public InputStream getInputStream() {
-            return message.getContent();
-        }
-
-        @Override
-        public long getMessageSize() {
-            return message.getSize();
-        }
-    }
-
-    private final MailSpool mailSpool;
-
-    @Inject
-    public MessageSender(MailSpool mailSpool) {
-        this.mailSpool = mailSpool;
-    }
-
-    public Mono<Void> sendMessage(MetaDataWithContent message, Envelope envelope, MailboxSession session) {
-        return Mono.usingWhen(Mono.fromCallable(() -> buildMail(message, envelope)),
-            mail -> sendMessage(message.getMessageId(), mail, session),
-            mail -> Mono.fromRunnable(() -> LifecycleUtil.dispose(mail)).subscribeOn(Schedulers.boundedElastic()));
-    }
-
-    @VisibleForTesting
-    static Mail buildMail(MetaDataWithContent message, Envelope envelope) throws MessagingException {
-        String name = message.getMessageId().serialize();
-        MailImpl mail = MailImpl.builder()
-            .name(name)
-            .sender(envelope.getFrom().asOptional().orElseThrow(() -> new RuntimeException("Sender is mandatory")))
-            .addRecipients(envelope.getRecipients())
-            .build();
-        mail.setMessageContent(new MessageMimeMessageSource(name, message));
-        return mail;
-    }
-
-    public Mono<Void> sendMessage(MessageId messageId, Mail mail, MailboxSession session) {
-        MailMetadata metadata = new MailMetadata(messageId, session.getUser().asString());
-        return mailSpool.send(mail, metadata);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ReferenceUpdater.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ReferenceUpdater.java
deleted file mode 100644
index 91f0c8b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ReferenceUpdater.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-import jakarta.mail.Flags;
-
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager.FlagsUpdateMode;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.Header;
-import org.apache.james.mailbox.model.Headers;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.util.streams.Iterators;
-import org.apache.mailet.base.RFC2822Headers;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class ReferenceUpdater {
-    public static final String X_FORWARDED_ID_HEADER = "X-Forwarded-Message-Id";
-    public static final Flags FORWARDED_FLAG = new Flags("$Forwarded");
-
-    private static final Logger logger = LoggerFactory.getLogger(ReferenceUpdater.class);
-
-    private final MessageIdManager messageIdManager;
-    private final MailboxManager mailboxManager;
-
-    @Inject
-    public ReferenceUpdater(MessageIdManager messageIdManager, MailboxManager mailboxManager) {
-        this.messageIdManager = messageIdManager;
-        this.mailboxManager = mailboxManager;
-    }
-
-    public Mono<Void> updateReferences(Headers headers, MailboxSession session) throws MailboxException {
-        Map<String, String> headersAsMap = Iterators.toStream(headers.headers())
-            .collect(ImmutableMap.toImmutableMap(Header::getName, Header::getValue));
-        return updateReferences(headersAsMap, session);
-    }
-
-    public Mono<Void> updateReferences(Map<String, String> headers, MailboxSession session) throws MailboxException {
-        Optional<String> inReplyToId = Optional.ofNullable(headers.get(RFC2822Headers.IN_REPLY_TO));
-        Optional<String> forwardedId = Optional.ofNullable(headers.get(X_FORWARDED_ID_HEADER));
-
-        return inReplyToId.map(Throwing.function((String id) -> updateAnswered(id, session)).sneakyThrow()).orElse(Mono.empty())
-            .then(forwardedId.map((Throwing.function((String id) -> updateForwarded(id, session)).sneakyThrow())).orElse(Mono.empty()));
-    }
-
-    private Mono<Void> updateAnswered(String messageId, MailboxSession session) throws MailboxException {
-        return updateFlag(messageId, session, new Flags(Flags.Flag.ANSWERED));
-    }
-
-    private Mono<Void> updateForwarded(String messageId, MailboxSession session) throws MailboxException {
-        return updateFlag(messageId, session, FORWARDED_FLAG);
-    }
-
-    private Mono<Void> updateFlag(String messageId, MailboxSession session, Flags flag) throws MailboxException {
-        int limit = 2;
-        MultimailboxesSearchQuery searchByRFC822MessageId = MultimailboxesSearchQuery
-            .from(SearchQuery.of(SearchQuery.mimeMessageID(messageId)))
-            .build();
-        return Flux.from(mailboxManager.search(searchByRFC822MessageId, session, limit))
-            .collectList()
-            .flatMap(references -> {
-                MessageId reference = Iterables.getOnlyElement(references);
-                return Flux.from(messageIdManager.messageMetadata(reference, session))
-                    .map(metaData -> metaData.getComposedMessageId().getMailboxId())
-                    .collect(ImmutableList.toImmutableList())
-                    .flatMap(mailboxIds -> Mono.from(messageIdManager.setFlagsReactive(flag, FlagsUpdateMode.ADD, reference, mailboxIds, session)));
-            })
-            .onErrorResume(NoSuchElementException.class, e -> {
-                logger.info("Unable to find a message with this Mime Message Id: " + messageId);
-                return Mono.empty();
-            })
-            .onErrorResume(IllegalArgumentException.class, e -> {
-                logger.info("Too many messages are matching this Mime Message Id: " + messageId);
-                return Mono.empty();
-            });
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/RequestHandler.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/RequestHandler.java
deleted file mode 100644
index 63d4fb0..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/RequestHandler.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.draft.model.AuthenticatedRequest;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.JmapResponseWriter;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.util.MDCBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class RequestHandler {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
-
-    private final JmapRequestParser jmapRequestParser;
-    private final JmapResponseWriter jmapResponseWriter;
-    private final Map<Method.Request.Name, Method> methods;
-
-    @Inject
-    public RequestHandler(Set<Method> methods, JmapRequestParser jmapRequestParser, JmapResponseWriter jmapResponseWriter) {
-        this.jmapRequestParser = jmapRequestParser;
-        this.jmapResponseWriter = jmapResponseWriter;
-        this.methods = methods.stream()
-                .collect(Collectors.toMap(Method::requestHandled, Function.identity()));
-    }
-
-    public Flux<InvocationResponse> handle(AuthenticatedRequest request) {
-        Optional<MailboxSession> mailboxSession = Optional.ofNullable(request.getMailboxSession());
-        try (Closeable closeable =
-                 MDCBuilder.create()
-                     .addToContextIfPresent(MDCBuilder.USER, mailboxSession.map(MailboxSession::getUser).map(Username::asString))
-                     .addToContextIfPresent(MDCBuilder.SESSION_ID, mailboxSession.map(MailboxSession::getSessionId)
-                        .map(MailboxSession.SessionId::getValue)
-                        .map(l -> Long.toString(l)))
-                     .addToContext(MDCBuilder.ACTION, request.getMethodName().getName())
-                     .build()) {
-            return Optional.ofNullable(methods.get(request.getMethodName()))
-                .map(extractAndProcess(request))
-                .map(jmapResponseWriter::formatMethodResponse)
-                .orElseThrow(() -> new IllegalStateException("unknown method " + request.getMethodName()));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-    
-    private Function<Method, Flux<JmapResponse>> extractAndProcess(AuthenticatedRequest request) {
-        MailboxSession mailboxSession = request.getMailboxSession();
-        return (Method method) ->
-            Mono.fromCallable(() -> jmapRequestParser.extractJmapRequest(request, method.requestType()))
-                .flatMapMany(jmapRequest -> method.process(jmapRequest, request.getMethodCallId(), mailboxSession))
-                .onErrorResume(JmapFieldNotSupportedException.class, e -> errorNotImplemented(e, request))
-                .onErrorResume(
-                    e -> e.getCause() instanceof JmapFieldNotSupportedException,
-                    e -> errorNotImplemented((JmapFieldNotSupportedException) e.getCause(), request))
-                .onErrorResume(IOException.class, e -> error(request, generateInvalidArgumentError(e.getMessage())));
-    }
-
-    public ErrorResponse generateInvalidArgumentError(String description) {
-        return ErrorResponse.builder()
-            .type("invalidArguments")
-            .description(description)
-            .build();
-    }
-
-    private Flux<JmapResponse> errorNotImplemented(JmapFieldNotSupportedException error, AuthenticatedRequest request) {
-        return Flux.just(
-                JmapResponse.builder()
-                    .methodCallId(request.getMethodCallId())
-                    .error(generateInvalidArgumentError("The field '" + error.getField() + "' of '" + error.getIssuer() + "' is not supported"))
-                    .build());
-    }
-
-    private Flux<JmapResponse> error(AuthenticatedRequest request, ErrorResponse error) {
-        return Flux.just(
-                JmapResponse.builder()
-                    .methodCallId(request.getMethodCallId())
-                    .error(error)
-                    .build());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java
deleted file mode 100644
index 7d2e643..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.io.IOException;
-import java.util.List;
-
-import jakarta.inject.Inject;
-import jakarta.mail.Flags;
-import jakarta.mail.MessagingException;
-
-import org.apache.james.jmap.draft.exceptions.InvalidOriginMessageForMDNException;
-import org.apache.james.jmap.draft.exceptions.MessageNotFoundException;
-import org.apache.james.jmap.draft.model.JmapMDN;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory.MetaDataWithContent;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SystemMailboxesProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.OverQuotaException;
-import org.apache.james.mailbox.model.FetchGroup;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.MessageResult;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.mime4j.codec.DecodeMonitor;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.field.ParseException;
-import org.apache.james.mime4j.message.DefaultMessageBuilder;
-import org.apache.james.mime4j.stream.MimeConfig;
-import org.apache.james.server.core.Envelope;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import reactor.core.publisher.Flux;
-
-public class SendMDNProcessor implements SetMessagesProcessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SendMDNProcessor.class);
-
-    private final MetricFactory metricFactory;
-    private final SystemMailboxesProvider systemMailboxesProvider;
-    private final MessageIdManager messageIdManager;
-    private final MessageAppender messageAppender;
-    private final MessageSender messageSender;
-
-    @Inject
-    public SendMDNProcessor(MetricFactory metricFactory, SystemMailboxesProvider systemMailboxesProvider,
-                            MessageIdManager messageIdManager, MessageAppender messageAppender, MessageSender messageSender) {
-        this.metricFactory = metricFactory;
-        this.systemMailboxesProvider = systemMailboxesProvider;
-        this.messageIdManager = messageIdManager;
-        this.messageAppender = messageAppender;
-        this.messageSender = messageSender;
-    }
-
-    @Override
-    public SetMessagesResponse process(SetMessagesRequest request, MailboxSession mailboxSession) {
-        if (request.getSendMDN().isEmpty()) {
-            return SetMessagesResponse.builder().build();
-        }
-        return metricFactory.decorateSupplierWithTimerMetric(JMAP_PREFIX + "SendMDN",
-            () -> handleMDNCreation(request, mailboxSession));
-    }
-
-    private SetMessagesResponse handleMDNCreation(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return request.getSendMDN()
-            .stream()
-            .map(MDNCreationEntry -> handleMDNCreation(MDNCreationEntry, mailboxSession))
-            .reduce(SetMessagesResponse.builder(), SetMessagesResponse.Builder::mergeWith)
-            .build();
-    }
-
-    private SetMessagesResponse.Builder handleMDNCreation(ValueWithId.MDNCreationEntry MDNCreationEntry, MailboxSession mailboxSession) {
-        try {
-            MessageId messageId = sendMdn(MDNCreationEntry, mailboxSession);
-            return SetMessagesResponse.builder()
-                .mdnSent(MDNCreationEntry.getCreationId(), messageId);
-        } catch (InvalidOriginMessageForMDNException e) {
-            return SetMessagesResponse.builder()
-                .mdnNotSent(MDNCreationEntry.getCreationId(),
-                    SetError.builder()
-                        .description(String.format("Origin messageId '%s' is invalid." +
-                                " A Message Delivery Notification can not be generated for it." +
-                                " Explanation: " + e.getExplanation(),
-                            MDNCreationEntry.getValue().getMessageId().serialize()))
-                        .type(SetError.Type.INVALID_ARGUMENTS)
-                        .build());
-
-        } catch (MessageNotFoundException e) {
-            return SetMessagesResponse.builder()
-                .mdnNotSent(MDNCreationEntry.getCreationId(),
-                    SetError.builder()
-                        .description(String.format("Message with id %s not found. Thus could not send MDN.",
-                            MDNCreationEntry.getValue().getMessageId().serialize()))
-                        .type(SetError.Type.INVALID_ARGUMENTS)
-                        .build());
-
-        } catch (OverQuotaException e) {
-            return SetMessagesResponse.builder()
-                .mdnNotSent(MDNCreationEntry.getCreationId(),
-                    SetError.builder()
-                        .description(e.getMessage())
-                        .type(SetError.Type.MAX_QUOTA_REACHED)
-                        .build());
-
-        } catch (Exception e) {
-            LOGGER.error("Error while sending MDN", e);
-            return SetMessagesResponse.builder()
-                .mdnNotSent(MDNCreationEntry.getCreationId(),
-                    SetError.builder()
-                        .description(String.format("Could not send MDN %s", MDNCreationEntry.getCreationId().getId()))
-                        .type(SetError.Type.ERROR)
-                        .build());
-        }
-    }
-
-    private MessageId sendMdn(ValueWithId.MDNCreationEntry MDNCreationEntry, MailboxSession mailboxSession)
-            throws MailboxException, IOException, MessagingException, ParseException, MessageNotFoundException, InvalidOriginMessageForMDNException {
-
-        JmapMDN mdn = MDNCreationEntry.getValue();
-        Message originalMessage = retrieveOriginalMessage(mdn, mailboxSession);
-
-        Message mdnAnswer = mdn.generateMDNMessage(originalMessage, mailboxSession);
-        originalMessage.dispose();
-
-        Flags seen = new Flags(Flags.Flag.SEEN);
-        MetaDataWithContent metaDataWithContent = messageAppender.appendMessageInMailbox(mdnAnswer,
-            getOutbox(mailboxSession), seen, mailboxSession);
-
-        messageSender.sendMessage(metaDataWithContent, Envelope.fromMime4JMessage(mdnAnswer), mailboxSession)
-            .block();
-
-        return metaDataWithContent.getMessageId();
-    }
-
-    private Message retrieveOriginalMessage(JmapMDN mdn, MailboxSession mailboxSession) throws MailboxException, IOException, MessageNotFoundException {
-        List<MessageResult> messages = messageIdManager.getMessage(mdn.getMessageId(), FetchGroup.HEADERS, mailboxSession);
-
-        if (messages.size() == 0) {
-            throw new MessageNotFoundException();
-        }
-
-        DefaultMessageBuilder messageBuilder = new DefaultMessageBuilder();
-        messageBuilder.setMimeEntityConfig(MimeConfig.PERMISSIVE);
-        messageBuilder.setDecodeMonitor(DecodeMonitor.SILENT);
-        return messageBuilder.parseMessage(messages.get(0).getHeaders().getInputStream());
-    }
-
-    private MessageManager getOutbox(MailboxSession mailboxSession) throws MailboxException {
-        return Flux.from(systemMailboxesProvider.getMailboxByRole(Role.OUTBOX, mailboxSession.getUser()))
-            .toStream()
-            .findAny()
-            .orElseThrow(() -> new IllegalStateException("User don't have an Outbox"));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java
deleted file mode 100644
index 32cbe92..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.filtering.FilteringManagement;
-import org.apache.james.jmap.api.filtering.Rule;
-import org.apache.james.jmap.draft.model.JmapRuleDTO;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetFilterRequest;
-import org.apache.james.jmap.draft.model.SetFilterResponse;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-import org.apache.james.util.ReactorUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetFilterMethod implements Method {
-
-    public static class DuplicatedRuleException extends Exception {
-        private final ImmutableList<Rule.Id> duplicatedIds;
-
-        public DuplicatedRuleException(ImmutableList<Rule.Id> duplicatedIds) {
-            super("The following rules were duplicated:" + format(duplicatedIds));
-            this.duplicatedIds = duplicatedIds;
-        }
-    }
-
-    public static class MultipleMailboxIdException extends Exception {
-        private final ImmutableList<Rule.Id> idsWithMultipleMailboxes;
-
-        public MultipleMailboxIdException(ImmutableList<Rule.Id> idsWithMultipleMailboxes) {
-            super("The following rules were targeting several mailboxes:" + format(idsWithMultipleMailboxes));
-            this.idsWithMultipleMailboxes = idsWithMultipleMailboxes;
-        }
-    }
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetFilterMethod.class);
-
-    private static final Request.Name METHOD_NAME = Request.name("setFilter");
-    private static final Response.Name RESPONSE_NAME = Response.name("filterSet");
-
-    private static String format(ImmutableList<Rule.Id> ids) {
-        return "[" + ids.stream()
-                .map(Rule.Id::asString)
-                .map(SetFilterMethod::quote)
-                .collect(Collectors.joining(","))
-                + "]";
-    }
-
-    private static String quote(String s) {
-        return "'" + s + "'";
-    }
-
-    private final MetricFactory metricFactory;
-    private final FilteringManagement filteringManagement;
-
-    @Inject
-    public SetFilterMethod(MetricFactory metricFactory, FilteringManagement filteringManagement) {
-        this.filteringManagement = filteringManagement;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return SetFilterRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(methodCallId);
-        Preconditions.checkNotNull(mailboxSession);
-
-        Preconditions.checkArgument(request instanceof SetFilterRequest);
-
-        SetFilterRequest setFilterRequest = (SetFilterRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(methodCallId, mailboxSession, setFilterRequest)
-                .contextWrite(jmapAction("SET_FILTER"))
-                .contextWrite(context("SET_FILTER", MDCBuilder.ofValue("update", setFilterRequest.getSingleton().toString())))));
-    }
-
-    private Mono<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, SetFilterRequest request) {
-        try {
-            return updateFilter(methodCallId, request, mailboxSession.getUser())
-                .doOnEach(ReactorUtils.logOnError(e -> LOGGER.warn("Failed setting Rules", e)))
-                .onErrorResume(e -> Mono.just(unknownError(methodCallId)));
-        } catch (MultipleMailboxIdException e) {
-            LOGGER.debug("Rule targeting several mailboxes", e);
-            return Mono.just(multipleMailboxesError(methodCallId, e));
-        }  catch (DuplicatedRuleException e) {
-            LOGGER.debug("Duplicated rules", e);
-            return Mono.just(duplicatedIdsError(methodCallId, e));
-        }  catch (IllegalArgumentException e) {
-            LOGGER.warn("IllegalArgumentException of setting Rules", e);
-            return Mono.just(invalidArgumentsError(methodCallId, e.getMessage()));
-        } catch (IllegalStateException e) {
-            LOGGER.warn("IllegalStateException of setting Rules", e);
-            return Mono.just(invalidArgumentsError(methodCallId, e.getMessage()));
-        } catch (Exception e) {
-            LOGGER.warn("Failed setting Rules", e);
-            return Mono.just(unknownError(methodCallId));
-        }
-    }
-
-    private Mono<JmapResponse> updateFilter(MethodCallId methodCallId, SetFilterRequest request, Username username) throws DuplicatedRuleException, MultipleMailboxIdException {
-        ImmutableList<Rule> rules = request.getSingleton().stream()
-            .map(JmapRuleDTO::toRule)
-            .collect(ImmutableList.toImmutableList());
-
-        ensureNoDuplicatedRules(rules);
-        ensureNoMultipleMailboxesRules(rules);
-
-        return Mono.from(filteringManagement.defineRulesForUser(username, rules, Optional.empty()))
-            .thenReturn(JmapResponse.builder()
-                .methodCallId(methodCallId)
-                .responseName(RESPONSE_NAME)
-                .response(SetFilterResponse.updated())
-                .build());
-    }
-
-    private void ensureNoMultipleMailboxesRules(ImmutableList<Rule> rules) throws MultipleMailboxIdException {
-        ImmutableList<Rule.Id> idWithMultipleMailboxes = rules.stream()
-            .filter(rule -> rule.getAction().getAppendInMailboxes().getMailboxIds().size() > 1)
-            .map(Rule::getId)
-            .collect(ImmutableList.toImmutableList());
-
-        if (!idWithMultipleMailboxes.isEmpty()) {
-            throw new MultipleMailboxIdException(idWithMultipleMailboxes);
-        }
-    }
-
-    private void ensureNoDuplicatedRules(List<Rule> rules) throws DuplicatedRuleException {
-        ImmutableList<Rule.Id> duplicatedIds = rules.stream()
-            .collect(ImmutableListMultimap.toImmutableListMultimap(
-                Rule::getId,
-                Function.identity()))
-            .asMap()
-            .entrySet()
-            .stream()
-            .filter(entry -> entry.getValue().size() > 1)
-            .map(Map.Entry::getKey)
-            .collect(ImmutableList.toImmutableList());
-
-        if (!duplicatedIds.isEmpty()) {
-            throw new DuplicatedRuleException(duplicatedIds);
-        }
-    }
-
-    private JmapResponse unknownError(MethodCallId methodCallId) {
-        return unknownError(methodCallId, "Failed to retrieve filter");
-    }
-
-    private JmapResponse invalidArgumentsError(MethodCallId methodCallId, String errorMessage) {
-        return JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(RESPONSE_NAME)
-            .response(ErrorResponse.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS.asString())
-                .description(errorMessage)
-                .build())
-            .build();
-    }
-
-    private JmapResponse unknownError(MethodCallId methodCallId, String errorMessage) {
-        return JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(RESPONSE_NAME)
-            .response(ErrorResponse.builder()
-                .type(SetError.Type.ERROR.asString())
-                .description(errorMessage)
-                .build())
-            .build();
-    }
-
-    private JmapResponse duplicatedIdsError(MethodCallId methodCallId, DuplicatedRuleException e) {
-        return JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(RESPONSE_NAME)
-            .response(SetFilterResponse.notUpdated(SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The following rules were duplicated: " + format(e.duplicatedIds))
-                .build()))
-            .build();
-    }
-
-    private JmapResponse multipleMailboxesError(MethodCallId methodCallId, MultipleMailboxIdException e) {
-        return JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(RESPONSE_NAME)
-            .response(SetFilterResponse.notUpdated(SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The following rules targeted several mailboxes, which is not supported: " + format(e.idsWithMultipleMailboxes))
-                .build()))
-            .build();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessor.java
deleted file mode 100644
index 3a246a8..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessor.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.exceptions.MailboxNotOwnedException;
-import org.apache.james.jmap.draft.exceptions.MailboxParentNotFoundException;
-import org.apache.james.jmap.draft.model.MailboxCreationId;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxCreateRequest;
-import org.apache.james.jmap.draft.utils.DependencyGraph.CycleDetectedException;
-import org.apache.james.jmap.draft.utils.SortingHierarchicalCollections;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.InboxAlreadyCreated;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.MailboxExistsException;
-import org.apache.james.mailbox.exception.MailboxNameException;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.exception.TooLongMailboxNameException;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxId.Factory;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.api.TimeMetric;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.github.fge.lambdas.functions.FunctionChainer;
-import com.google.common.annotations.VisibleForTesting;
-
-public class SetMailboxesCreationProcessor implements SetMailboxesProcessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
-
-    private final MailboxManager mailboxManager;
-    private final SortingHierarchicalCollections<Map.Entry<MailboxCreationId, MailboxCreateRequest>, MailboxCreationId> sortingHierarchicalCollections;
-    private final MailboxFactory mailboxFactory;
-    private final Factory mailboxIdFactory;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    SetMailboxesCreationProcessor(MailboxManager mailboxManager, MailboxFactory mailboxFactory, Factory mailboxIdFactory, MetricFactory metricFactory) {
-        this.mailboxManager = mailboxManager;
-        this.metricFactory = metricFactory;
-        this.sortingHierarchicalCollections =
-            new SortingHierarchicalCollections<>(
-                Map.Entry::getKey,
-                x -> x.getValue().getParentId());
-        this.mailboxFactory = mailboxFactory;
-        this.mailboxIdFactory = mailboxIdFactory;
-    }
-
-    @Override
-    public SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        TimeMetric timeMetric = metricFactory.timer(JMAP_PREFIX + "SetMailboxesCreationProcessor");
-
-        SetMailboxesResponse.Builder builder = SetMailboxesResponse.builder();
-        try {
-            Map<MailboxCreationId, MailboxId> creationIdsToCreatedMailboxId = new HashMap<>();
-            sortingHierarchicalCollections.sortFromRootToLeaf(request.getCreate().entrySet())
-                .forEach(entry -> 
-                    createMailbox(entry.getKey(), entry.getValue(), mailboxSession, creationIdsToCreatedMailboxId, builder));
-        } catch (CycleDetectedException e) {
-            markRequestsAsNotCreatedDueToCycle(request, builder);
-        }
-
-        timeMetric.stopAndPublish();
-        return builder.build();
-    }
-
-    private void markRequestsAsNotCreatedDueToCycle(SetMailboxesRequest request, SetMailboxesResponse.Builder builder) {
-        request.getCreate().forEach((key, value) ->
-            builder.notCreated(
-                key,
-                SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description("The created mailboxes introduce a cycle.")
-                    .build()));
-    }
-
-    private void createMailbox(MailboxCreationId mailboxCreationId, MailboxCreateRequest mailboxRequest, MailboxSession mailboxSession,
-            Map<MailboxCreationId, MailboxId> creationIdsToCreatedMailboxId, SetMailboxesResponse.Builder builder) {
-        try {
-            ensureValidMailboxName(mailboxRequest, mailboxSession);
-            MailboxPath mailboxPath = computeMailboxPath(mailboxRequest, creationIdsToCreatedMailboxId, mailboxSession);
-            Optional<MailboxId> mailboxId = mailboxManager.createMailbox(mailboxPath, MailboxManager.CreateOption.CREATE_SUBSCRIPTION, mailboxSession);
-            Optional<Mailbox> mailbox = mailboxId.flatMap(id -> mailboxFactory.builder()
-                    .id(id)
-                    .session(mailboxSession)
-                    .build()
-                    .blockOptional());
-            if (mailbox.isPresent()) {
-                builder.created(mailboxCreationId, mailbox.get());
-                creationIdsToCreatedMailboxId.put(mailboxCreationId, mailbox.get().getId());
-            } else {
-                builder.notCreated(mailboxCreationId, SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("An error occurred when creating the mailbox")
-                    .build());
-            }
-        } catch (TooLongMailboxNameException e) {
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The mailbox name length is too long")
-                .build());
-        } catch (MailboxNotOwnedException e) {
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The mailbox can not be created with a parent mailbox belonging to another user")
-                .build());
-        } catch (MailboxNameException | MailboxParentNotFoundException e) {
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description(e.getMessage())
-                    .build());
-        } catch (InboxAlreadyCreated e) {
-            String message = String.format("The mailbox '%s' already exists as 'INBOX'", e.getMailboxName());
-            LOGGER.error(message, e);
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description(message)
-                .build());
-        } catch (MailboxExistsException e) {
-            String message = String.format("The mailbox '%s' already exists.", mailboxCreationId.getCreationId());
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description(message)
-                    .build());
-        } catch (MailboxException e) {
-            String message = String.format("An error occurred when creating the mailbox '%s'", mailboxCreationId.getCreationId());
-            LOGGER.error(message, e);
-            builder.notCreated(mailboxCreationId, SetError.builder()
-                .type(SetError.Type.ERROR)
-                .description(message)
-                .build());
-        }
-    }
-
-    private void ensureValidMailboxName(MailboxCreateRequest mailboxRequest, MailboxSession mailboxSession) throws MailboxNameException {
-        String name = mailboxRequest.getName();
-        char pathDelimiter = mailboxSession.getPathDelimiter();
-        if (name.contains(String.valueOf(pathDelimiter))) {
-            throw new MailboxNameException(String.format("The mailbox '%s' contains an illegal character: '%c'", name, pathDelimiter));
-        }
-    }
-
-    private MailboxPath computeMailboxPath(MailboxCreateRequest mailboxRequest, Map<MailboxCreationId, MailboxId> creationIdsToCreatedMailboxId, MailboxSession mailboxSession) throws MailboxException {
-        if (mailboxRequest.getParentId().isPresent()) {
-            MailboxCreationId parentId = mailboxRequest.getParentId().get();
-            MailboxPath parentPath = getMailboxPath(creationIdsToCreatedMailboxId, mailboxSession, parentId);
-
-            assertBelongsToUser(parentPath, mailboxSession);
-
-            return MailboxPath.forUser(mailboxSession.getUser(),
-                parentPath.getName() + mailboxSession.getPathDelimiter() + mailboxRequest.getName());
-        }
-        return MailboxPath.forUser(mailboxSession.getUser(), mailboxRequest.getName());
-    }
-
-    private void assertBelongsToUser(MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxNotOwnedException {
-        if (!mailboxPath.belongsTo(mailboxSession)) {
-            throw new MailboxNotOwnedException();
-        }
-    }
-
-    private MailboxPath getMailboxPath(Map<MailboxCreationId, MailboxId> creationIdsToCreatedMailboxId, MailboxSession mailboxSession, MailboxCreationId parentId) throws MailboxException {
-        Optional<MailboxId> mailboxId = readCreationIdAsMailboxId(parentId)
-            .or(() -> Optional.ofNullable(creationIdsToCreatedMailboxId.get(parentId)));
-
-        return getMailboxPathFromId(mailboxId, mailboxSession)
-                .orElseThrow(() -> new MailboxParentNotFoundException(parentId));
-    }
-
-    private Optional<MailboxId> readCreationIdAsMailboxId(MailboxCreationId creationId) {
-        try {
-            return Optional.of(mailboxIdFactory.fromString(creationId.getCreationId()));
-        } catch (Exception e) {
-            return Optional.empty();
-        }
-    }
-
-    @VisibleForTesting
-    Optional<MailboxPath> getMailboxPathFromId(Optional<MailboxId> mailboxId, MailboxSession mailboxSession) {
-        FunctionChainer<MailboxId, Optional<MailboxPath>> fromMailboxIdToMailboxPath = Throwing.function(id -> {
-            try {
-                return Optional.of(mailboxManager.getMailbox(id, mailboxSession).getMailboxPath());
-            } catch (MailboxNotFoundException e) {
-                return Optional.empty();
-            }
-        });
-        return mailboxId
-            .flatMap(fromMailboxIdToMailboxPath.sneakyThrow());
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesDestructionProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesDestructionProcessor.java
deleted file mode 100644
index 1cadec1..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesDestructionProcessor.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.exceptions.MailboxHasChildException;
-import org.apache.james.jmap.draft.exceptions.SystemMailboxNotUpdatableException;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse.Builder;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.utils.MailboxUtils;
-import org.apache.james.jmap.draft.utils.SortingHierarchicalCollections;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SubscriptionManager;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.TooLongMailboxNameException;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.api.TimeMetric;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMailboxesDestructionProcessor implements SetMailboxesProcessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMailboxesDestructionProcessor.class);
-
-    private final MailboxManager mailboxManager;
-    private final SubscriptionManager subscriptionManager;
-    private final SortingHierarchicalCollections<Map.Entry<MailboxId, Mailbox>, MailboxId> sortingHierarchicalCollections;
-    private final MailboxUtils mailboxUtils;
-    private final MailboxFactory mailboxFactory;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    SetMailboxesDestructionProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, MailboxUtils mailboxUtils, MailboxFactory mailboxFactory, MetricFactory metricFactory) {
-        this.mailboxManager = mailboxManager;
-        this.subscriptionManager = subscriptionManager;
-        this.metricFactory = metricFactory;
-        this.sortingHierarchicalCollections =
-            new SortingHierarchicalCollections<>(
-                    Entry::getKey,
-                    x -> x.getValue().getParentId());
-        this.mailboxUtils = mailboxUtils;
-        this.mailboxFactory = mailboxFactory;
-    }
-
-    @Override
-    public SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        TimeMetric timeMetric = metricFactory.timer(JMAP_PREFIX + "SetMailboxesDestructionProcessor");
-        ImmutableMap<MailboxId, Mailbox> idToMailbox = mapDestroyRequests(request, mailboxSession);
-
-        SetMailboxesResponse.Builder builder = SetMailboxesResponse.builder();
-        sortingHierarchicalCollections.sortFromLeafToRoot(idToMailbox.entrySet())
-            .forEach(entry -> destroyMailbox(entry, mailboxSession, builder));
-
-        notDestroyedRequests(request, idToMailbox, builder);
-        timeMetric.stopAndPublish();
-        return builder.build();
-    }
-
-    private ImmutableMap<MailboxId, Mailbox> mapDestroyRequests(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        ImmutableMap.Builder<MailboxId, Mailbox> idToMailboxBuilder = ImmutableMap.builder(); 
-        request.getDestroy().stream()
-            .map(id -> mailboxFactory.builder()
-                    .id(id)
-                    .session(mailboxSession)
-                    .build()
-                    .blockOptional())
-            .flatMap(Optional::stream)
-            .forEach(mailbox -> idToMailboxBuilder.put(mailbox.getId(), mailbox));
-        return idToMailboxBuilder.build();
-    }
-
-    private void notDestroyedRequests(SetMailboxesRequest request, ImmutableMap<MailboxId, Mailbox> idToMailbox, SetMailboxesResponse.Builder builder) {
-        request.getDestroy().stream()
-            .filter(id -> !idToMailbox.containsKey(id))
-            .forEach(id -> notDestroy(id, builder));
-    }
-
-    private void destroyMailbox(Entry<MailboxId, Mailbox> entry, MailboxSession mailboxSession, SetMailboxesResponse.Builder builder) {
-        try {
-            Mailbox mailbox = entry.getValue();
-            preconditions(mailbox, mailboxSession);
-
-            MailboxPath deletedMailbox = mailboxManager.deleteMailbox(mailbox.getId(), mailboxSession).generateAssociatedPath();
-            subscriptionManager.unsubscribe(mailboxSession, deletedMailbox);
-            builder.destroyed(entry.getKey());
-        } catch (MailboxHasChildException e) {
-            builder.notDestroyed(entry.getKey(), SetError.builder()
-                    .type(SetError.Type.MAILBOX_HAS_CHILD)
-                    .description(String.format("The mailbox '%s' has a child.", entry.getKey().serialize()))
-                    .build());
-        } catch (SystemMailboxNotUpdatableException e) {
-            builder.notDestroyed(entry.getKey(), SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description(String.format("The mailbox '%s' is a system mailbox.", entry.getKey().serialize()))
-                .build());
-        } catch (TooLongMailboxNameException e) {
-            builder.notDestroyed(entry.getKey(), SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The mailbox name length is too long")
-                .build());
-        } catch (MailboxException e) {
-            String message = String.format("An error occurred when deleting the mailbox '%s'", entry.getKey().serialize());
-            LOGGER.error(message, e);
-            builder.notDestroyed(entry.getKey(), SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description(message)
-                    .build());
-        }
-    }
-
-    private void preconditions(Mailbox mailbox, MailboxSession mailboxSession) throws MailboxHasChildException, SystemMailboxNotUpdatableException, MailboxException {
-        checkForChild(mailbox.getId(), mailboxSession);
-        checkRole(mailbox.getRole());
-    }
-
-    private void checkForChild(MailboxId id, MailboxSession mailboxSession) throws MailboxHasChildException, MailboxException {
-        if (mailboxUtils.hasChildren(id, mailboxSession)) {
-            throw new MailboxHasChildException();
-        }
-    }
-
-    private void checkRole(Optional<Role> role) throws SystemMailboxNotUpdatableException {
-        if (role.map(Role::isSystemRole).orElse(false)) {
-            throw new SystemMailboxNotUpdatableException();
-        }
-    }
-
-    private void notDestroy(MailboxId id, Builder builder) {
-        builder.notDestroyed(id, SetError.builder()
-                .type(SetError.Type.NOT_FOUND)
-                .description(String.format("The mailbox '%s' was not found.", id.serialize()))
-                .build());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java
deleted file mode 100644
index f725fa4..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.util.MDCBuilder.ACTION;
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.Set;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetMailboxesMethod implements Method {
-
-    private static final Request.Name METHOD_NAME = Request.name("setMailboxes");
-    @VisibleForTesting static final Response.Name RESPONSE_NAME = Response.name("mailboxesSet");
-
-    private final Set<SetMailboxesProcessor> processors;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    public SetMailboxesMethod(Set<SetMailboxesProcessor> processors, MetricFactory metricFactory) {
-        this.processors = processors;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return SetMailboxesRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(methodCallId);
-        Preconditions.checkNotNull(mailboxSession);
-        Preconditions.checkArgument(request instanceof SetMailboxesRequest);
-
-        SetMailboxesRequest setMailboxesRequest = (SetMailboxesRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            setMailboxesResponse(setMailboxesRequest, mailboxSession)
-                .map(response -> JmapResponse.builder().methodCallId(methodCallId)
-                    .response(response)
-                    .responseName(RESPONSE_NAME)
-                    .build())))
-            .contextWrite(context(ACTION, mdc(setMailboxesRequest)));
-    }
-
-    private MDCBuilder mdc(SetMailboxesRequest setMailboxesRequest) {
-        return MDCBuilder.create()
-            .addToContext(MDCBuilder.ACTION, "SET_MAILBOXES")
-            .addToContext("create", setMailboxesRequest.getCreate().toString())
-            .addToContext("update", setMailboxesRequest.getUpdate().toString())
-            .addToContext("destroy", setMailboxesRequest.getDestroy().toString());
-    }
-
-    private Mono<SetMailboxesResponse> setMailboxesResponse(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        return Flux.fromIterable(processors)
-            .flatMap(processor -> processor.processReactive(request, mailboxSession))
-            .reduce(SetMailboxesResponse.builder(),
-                (builder, resp) -> resp.mergeInto(builder))
-            .map(SetMailboxesResponse.Builder::build);
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesProcessor.java
deleted file mode 100644
index 9a0c07d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesProcessor.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.util.ReactorUtils;
-
-import reactor.core.publisher.Mono;
-
-public interface SetMailboxesProcessor {
-
-    SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession);
-
-
-    default Mono<SetMailboxesResponse> processReactive(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        return Mono.fromCallable(() -> process(request, mailboxSession))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessor.java
deleted file mode 100644
index 025be04..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessor.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.exceptions.MailboxHasChildException;
-import org.apache.james.jmap.draft.exceptions.MailboxNotOwnedException;
-import org.apache.james.jmap.draft.exceptions.MailboxParentNotFoundException;
-import org.apache.james.jmap.draft.exceptions.SystemMailboxNotUpdatableException;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse.Builder;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxUpdateRequest;
-import org.apache.james.jmap.draft.utils.MailboxUtils;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.exception.DifferentDomainException;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.MailboxExistsException;
-import org.apache.james.mailbox.exception.MailboxNameException;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.exception.TooLongMailboxNameException;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.api.TimeMetric;
-import org.apache.james.util.OptionalUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.github.fge.lambdas.functions.ThrowingFunction;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-
-public class SetMailboxesUpdateProcessor implements SetMailboxesProcessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMailboxesUpdateProcessor.class);
-    private final MailboxUtils mailboxUtils;
-    private final MailboxManager mailboxManager;
-    private final MailboxFactory mailboxFactory;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    SetMailboxesUpdateProcessor(MailboxUtils mailboxUtils, MailboxManager mailboxManager, MailboxFactory mailboxFactory, MetricFactory metricFactory) {
-        this.mailboxUtils = mailboxUtils;
-        this.mailboxManager = mailboxManager;
-        this.mailboxFactory = mailboxFactory;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession) {
-        TimeMetric timeMetric = metricFactory.timer(JMAP_PREFIX + "mailboxUpdateProcessor");
-
-        SetMailboxesResponse.Builder responseBuilder = SetMailboxesResponse.builder();
-        request.getUpdate()
-            .forEach((key, value) -> handleUpdate(key, value, responseBuilder, mailboxSession));
-        timeMetric.stopAndPublish();
-        return responseBuilder.build();
-    }
-
-    private void handleUpdate(MailboxId mailboxId, MailboxUpdateRequest updateRequest, Builder responseBuilder, MailboxSession mailboxSession) {
-        try {
-            validateMailboxName(updateRequest, mailboxSession);
-            Mailbox mailbox = getMailbox(mailboxId, mailboxSession);
-            assertNotSharedOutboxOrDraftMailbox(mailbox, updateRequest);
-            assertSystemMailboxesAreNotUpdated(mailbox, updateRequest);
-            validateParent(mailbox, updateRequest, mailboxSession);
-
-            updateMailbox(mailbox, updateRequest, mailboxSession);
-            responseBuilder.updated(mailboxId);
-
-        } catch (SystemMailboxNotUpdatableException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description("Cannot update a system mailbox.")
-                    .build());
-        } catch (TooLongMailboxNameException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("The mailbox name length is too long")
-                .build());
-        } catch (MailboxNameException | IllegalArgumentException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description(e.getMessage())
-                    .build());
-        } catch (MailboxNotFoundException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.NOT_FOUND)
-                    .description(String.format("The mailbox '%s' was not found", mailboxId.serialize()))
-                    .build());
-        } catch (MailboxParentNotFoundException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.NOT_FOUND)
-                    .description(String.format("The parent mailbox '%s' was not found.", e.getParentId()))
-                    .build());
-        } catch (MailboxHasChildException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description("Cannot update a parent mailbox.")
-                    .build());
-        } catch (MailboxNotOwnedException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description("Parent mailbox is not owned.")
-                    .build());
-        } catch (MailboxExistsException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description("Cannot rename a mailbox to an already existing mailbox.")
-                    .build());
-        } catch (DifferentDomainException e) {
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                .type(SetError.Type.INVALID_ARGUMENTS)
-                .description("Cannot share a mailbox to another domain")
-                .build());
-        } catch (MailboxException e) {
-            LOGGER.error("Error while updating mailbox", e);
-            responseBuilder.notUpdated(mailboxId, SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("An error occurred when updating the mailbox")
-                    .build());
-        }
-    }
-
-    private void assertNotSharedOutboxOrDraftMailbox(Mailbox mailbox, MailboxUpdateRequest updateRequest) {
-        Preconditions.checkArgument(!updateRequest.getSharedWith().isPresent() || !mailbox.hasRole(Role.OUTBOX), "Sharing 'Outbox' is forbidden");
-        Preconditions.checkArgument(!updateRequest.getSharedWith().isPresent() || !mailbox.hasRole(Role.DRAFTS), "Sharing 'Draft' is forbidden");
-    }
-
-    private void assertSystemMailboxesAreNotUpdated(Mailbox mailbox, MailboxUpdateRequest updateRequest) throws SystemMailboxNotUpdatableException {
-        if (mailbox.hasSystemRole()) {
-            if (OptionalUtils.containsDifferent(updateRequest.getName(), mailbox.getName())
-                || requestChanged(updateRequest.getParentId(), mailbox.getParentId())
-                || requestChanged(updateRequest.getRole(), mailbox.getRole())
-                || OptionalUtils.containsDifferent(updateRequest.getSortOrder(), mailbox.getSortOrder())) {
-                throw new SystemMailboxNotUpdatableException();
-            }
-        }
-    }
-
-    @VisibleForTesting
-    <T> boolean requestChanged(Optional<T> requestValue, Optional<T> storeValue) {
-        return requestValue.isPresent() && !requestValue.equals(storeValue);
-    }
-
-    private Mailbox getMailbox(MailboxId mailboxId, MailboxSession mailboxSession) throws MailboxNotFoundException {
-        return mailboxFactory.builder()
-                .id(mailboxId)
-                .session(mailboxSession)
-                .build()
-                .blockOptional()
-                .orElseThrow(() -> new MailboxNotFoundException(mailboxId));
-    }
-
-    private void validateMailboxName(MailboxUpdateRequest updateRequest, MailboxSession mailboxSession) throws MailboxNameException {
-        char pathDelimiter = mailboxSession.getPathDelimiter();
-
-        if (nameContainsPathDelimiter(updateRequest, pathDelimiter)) {
-            throw new MailboxNameException(String.format("The mailbox '%s' contains an illegal character: '%c'", updateRequest.getName().get(), pathDelimiter));
-        }
-        if (nameMatchesSystemMailbox(updateRequest)) {
-            throw new MailboxNameException(String.format("The mailbox '%s' is a system mailbox.", updateRequest.getName().get()));
-        }
-    }
-
-    private boolean nameMatchesSystemMailbox(MailboxUpdateRequest updateRequest) {
-        return updateRequest.getName()
-                .flatMap(Role::from)
-                .filter(Role::isSystemRole)
-                .isPresent();
-    }
-
-    private boolean nameContainsPathDelimiter(MailboxUpdateRequest updateRequest, char pathDelimiter) {
-        return updateRequest.getName()
-                .filter(name -> name.contains(String.valueOf(pathDelimiter)))
-                .isPresent();
-    }
-
-    private void validateParent(Mailbox mailbox, MailboxUpdateRequest updateRequest, MailboxSession mailboxSession) throws MailboxException, MailboxHasChildException {
-        if (isParentIdInRequest(updateRequest)) {
-            MailboxId newParentId = updateRequest.getParentId().get();
-            MessageManager parent = retrieveParent(mailboxSession, newParentId);
-            if (mustChangeParent(mailbox.getParentId(), newParentId)) {
-                assertNoChildren(mailbox, mailboxSession);
-                assertOwned(mailboxSession, parent);
-            }
-        }
-    }
-
-    private void assertNoChildren(Mailbox mailbox, MailboxSession mailboxSession) throws MailboxException, MailboxHasChildException {
-        if (mailboxUtils.hasChildren(mailbox.getId(), mailboxSession)) {
-            throw new MailboxHasChildException();
-        }
-    }
-
-    private void assertOwned(MailboxSession mailboxSession, MessageManager parent) throws MailboxException {
-        if (!parent.getMailboxPath().belongsTo(mailboxSession)) {
-            throw new MailboxNotOwnedException();
-        }
-    }
-
-    private MessageManager retrieveParent(MailboxSession mailboxSession, MailboxId newParentId) throws MailboxException {
-        try {
-            return mailboxManager.getMailbox(newParentId, mailboxSession);
-        } catch (MailboxNotFoundException e) {
-            throw new MailboxParentNotFoundException(newParentId);
-        }
-    }
-
-    private boolean isParentIdInRequest(MailboxUpdateRequest updateRequest) {
-        return updateRequest.getParentId() != null
-                && updateRequest.getParentId().isPresent();
-    }
-
-    private boolean mustChangeParent(Optional<MailboxId> currentParentId, MailboxId newParentId) {
-        return currentParentId
-                .map(x -> ! x.equals(newParentId))
-                .orElse(true);
-    }
-
-    private void updateMailbox(Mailbox mailbox, MailboxUpdateRequest updateRequest, MailboxSession mailboxSession) throws MailboxException {
-        MailboxPath originMailboxPath = mailboxManager.getMailbox(mailbox.getId(), mailboxSession).getMailboxPath();
-        MailboxPath destinationMailboxPath = computeNewMailboxPath(mailbox, originMailboxPath, updateRequest, mailboxSession);
-
-        if (updateRequest.getSharedWith().isPresent()) {
-            mailboxManager.setRights(mailbox.getId(),
-                updateRequest.getSharedWith()
-                    .get()
-                    .removeEntriesFor(originMailboxPath.getUser())
-                    .toMailboxAcl(),
-                mailboxSession);
-        }
-
-        if (!originMailboxPath.equals(destinationMailboxPath)) {
-            mailboxManager.renameMailbox(mailbox.getId(), destinationMailboxPath, MailboxManager.RenameOption.RENAME_SUBSCRIPTIONS, mailboxSession);
-        }
-    }
-
-    private MailboxPath computeNewMailboxPath(Mailbox mailbox, MailboxPath originMailboxPath, MailboxUpdateRequest updateRequest, MailboxSession mailboxSession) throws MailboxException {
-        Optional<MailboxId> parentId = updateRequest.getParentId();
-        if (parentId == null) {
-            return MailboxPath.forUser(
-                mailboxSession.getUser(),
-                updateRequest.getName().orElse(mailbox.getName()));
-        }
-
-        MailboxPath modifiedMailboxPath = updateRequest.getName()
-                .map(newName -> computeMailboxPathWithNewName(originMailboxPath, newName))
-                .orElse(originMailboxPath);
-        ThrowingFunction<MailboxId, MailboxPath> computeNewMailboxPath = parentMailboxId -> computeMailboxPathWithNewParentId(modifiedMailboxPath, parentMailboxId, mailboxSession);
-        return parentId
-                .map(Throwing.function(computeNewMailboxPath).sneakyThrow())
-                .orElse(modifiedMailboxPath);
-    }
-
-    private MailboxPath computeMailboxPathWithNewName(MailboxPath originMailboxPath, String newName) {
-        return new MailboxPath(originMailboxPath, newName);
-    }
-
-    private MailboxPath computeMailboxPathWithNewParentId(MailboxPath originMailboxPath, MailboxId parentMailboxId, MailboxSession mailboxSession) throws MailboxException {
-        MailboxPath newParentMailboxPath = mailboxManager.getMailbox(parentMailboxId, mailboxSession).getMailboxPath();
-        String lastName = getCurrentMailboxName(originMailboxPath, mailboxSession);
-        return new MailboxPath(originMailboxPath, newParentMailboxPath.getName() + mailboxSession.getPathDelimiter() + lastName);
-    }
-
-    private String getCurrentMailboxName(MailboxPath originMailboxPath, MailboxSession mailboxSession) {
-        return Iterables.getLast(
-                Splitter.on(mailboxSession.getPathDelimiter())
-                    .splitToList(originMailboxPath.getName()));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java
deleted file mode 100644
index 175e3b3..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-import jakarta.mail.MessagingException;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.AttachmentsNotFoundException;
-import org.apache.james.jmap.draft.exceptions.InvalidDraftKeywordsException;
-import org.apache.james.jmap.draft.exceptions.InvalidMailboxForCreationException;
-import org.apache.james.jmap.draft.exceptions.MailboxNotOwnedException;
-import org.apache.james.jmap.draft.exceptions.SizeExceededException;
-import org.apache.james.jmap.draft.methods.ValueWithId.CreationMessageEntry;
-import org.apache.james.jmap.draft.methods.ValueWithId.MessageWithId;
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessage.DraftEmailer;
-import org.apache.james.jmap.draft.model.EnvelopeUtils;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMessagesError;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.draft.model.SetMessagesResponse.Builder;
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SystemMailboxesProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.exception.OverQuotaException;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.rrt.api.CanSendFrom;
-import org.apache.james.server.core.Envelope;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-
-public class SetMessagesCreationProcessor implements SetMessagesProcessor {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
-    private final MessageFullViewFactory messageFullViewFactory;
-    private final SystemMailboxesProvider systemMailboxesProvider;
-    private final MetricFactory metricFactory;
-    private final MailboxManager mailboxManager;
-    private final MailboxId.Factory mailboxIdFactory;
-    private final MessageAppender messageAppender;
-    private final MessageSender messageSender;
-    private final ReferenceUpdater referenceUpdater;
-    private final CanSendFrom canSendFrom;
-
-    @VisibleForTesting
-    @Inject
-    SetMessagesCreationProcessor(MessageFullViewFactory messageFullViewFactory,
-                                 SystemMailboxesProvider systemMailboxesProvider,
-                                 MetricFactory metricFactory,
-                                 MailboxManager mailboxManager,
-                                 MailboxId.Factory mailboxIdFactory,
-                                 MessageAppender messageAppender,
-                                 MessageSender messageSender,
-                                 ReferenceUpdater referenceUpdater,
-                                 CanSendFrom canSendFrom) {
-        this.messageFullViewFactory = messageFullViewFactory;
-        this.systemMailboxesProvider = systemMailboxesProvider;
-        this.metricFactory = metricFactory;
-        this.mailboxManager = mailboxManager;
-        this.mailboxIdFactory = mailboxIdFactory;
-        this.messageAppender = messageAppender;
-        this.messageSender = messageSender;
-        this.referenceUpdater = referenceUpdater;
-        this.canSendFrom = canSendFrom;
-    }
-
-    @Override
-    public Mono<SetMessagesResponse> processReactive(SetMessagesRequest request, MailboxSession mailboxSession) {
-        if (request.getCreate().isEmpty()) {
-            return Mono.just(SetMessagesResponse.builder().build());
-        }
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + "SetMessageCreationProcessor",
-            Flux.fromIterable(request.getCreate())
-                .flatMap(create -> handleCreate(create, mailboxSession))
-                .reduce(Builder::mergeWith)
-                .switchIfEmpty(Mono.just(SetMessagesResponse.builder()))
-                .map(Builder::build)));
-    }
-
-    private Mono<Builder> handleCreate(CreationMessageEntry create, MailboxSession mailboxSession) {
-        List<MailboxId> mailboxIds = toMailboxIds(create);
-
-        if (mailboxIds.isEmpty()) {
-            return Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.INVALID_PROPERTIES)
-                    .properties(MessageProperty.mailboxIds)
-                    .description("Message needs to be in at least one mailbox")
-                    .build()));
-        }
-
-        return assertIsUserOwnerOfMailboxes(mailboxIds, mailboxSession)
-            .then(performCreate(create, mailboxSession))
-            .onErrorResume(MailboxSendingNotAllowedException.class, e -> {
-                LOG.debug("{} is not allowed to send a mail using {} identity", e.getConnectedUser().asString(), e.getFromField());
-
-                return Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                    SetError.builder()
-                        .type(SetError.Type.INVALID_PROPERTIES)
-                        .properties(MessageProperty.from)
-                        .description("Invalid 'from' field. One accepted value is " +
-                            e.getConnectedUser().asString())
-                        .build()));
-            })
-            .onErrorResume(InvalidDraftKeywordsException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.INVALID_PROPERTIES)
-                    .properties(MessageProperty.keywords)
-                    .description(e.getMessage())
-                    .build())))
-            .onErrorResume(SizeExceededException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description(e.getMessage())
-                    .build())))
-            .onErrorResume(AttachmentsNotFoundException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetMessagesError.builder()
-                    .type(SetError.Type.INVALID_PROPERTIES)
-                    .properties(MessageProperty.attachments)
-                    .attachmentsNotFound(e.getAttachmentIds())
-                    .description("Attachment not found")
-                    .build())))
-            .onErrorResume(InvalidMailboxForCreationException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.INVALID_PROPERTIES)
-                    .properties(MessageProperty.mailboxIds)
-                    .description("Message creation is only supported in mailboxes with role Draft and Outbox")
-                    .build())))
-            .onErrorResume(MailboxInvalidMessageCreationException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                buildSetErrorFromValidationResult(create.getValue().validate()))))
-            .onErrorResume(MailboxNotFoundException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description(e.getMessage())
-                    .build())))
-            .onErrorResume(MailboxNotOwnedException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .properties(MessageProperty.mailboxIds)
-                    .description("MailboxId invalid")
-                    .build())))
-            .onErrorResume(OverQuotaException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.MAX_QUOTA_REACHED)
-                    .description(e.getMessage())
-                    .build())))
-            .onErrorResume(MailboxException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("unexpected error")
-                    .build())))
-            .onErrorResume(MessagingException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("unexpected error")
-                    .build())))
-            .onErrorResume(IOException.class, e -> Mono.just(SetMessagesResponse.builder().notCreated(create.getCreationId(),
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("unexpected error")
-                    .build())));
-    }
-
-    private ImmutableList<MailboxId> toMailboxIds(CreationMessageEntry create) {
-        return create.getValue().getMailboxIds()
-            .stream()
-            .distinct()
-            .map(mailboxIdFactory::fromString)
-            .collect(ImmutableList.toImmutableList());
-    }
-
-    private Mono<Builder> performCreate(CreationMessageEntry entry, MailboxSession session) {
-        return isAppendToMailboxWithRole(Role.OUTBOX, entry.getValue(), session)
-            .flatMap(isAppendToMailboxWithRole -> {
-                if (isAppendToMailboxWithRole) {
-                    return sendMailViaOutbox(entry, session);
-                } else if (entry.getValue().isDraft()) {
-                    return assertNoOutbox(entry, session)
-                        .then(saveDraft(entry, session));
-                } else {
-                    return isAppendToMailboxWithRole(Role.DRAFTS, entry.getValue(), session)
-                        .handle((isAppendedToDraft, sink) -> {
-                            if (isAppendedToDraft) {
-                                sink.error(new InvalidDraftKeywordsException("A draft message should be flagged as Draft"));
-                            } else {
-                                sink.error(new InvalidMailboxForCreationException("The only implemented feature is sending via outbox and draft saving"));
-                            }
-                        });
-                }
-            });
-    }
-
-    private Mono<Void> assertNoOutbox(CreationMessageEntry entry, MailboxSession session) {
-        return isTargettingAMailboxWithRole(Role.OUTBOX, entry.getValue(), session)
-            .handle((targetsOutbox, sink) -> {
-                if (targetsOutbox) {
-                    sink.error(new InvalidMailboxForCreationException("Mailbox ids can combine Outbox with other mailbox"));
-                }
-            });
-    }
-
-    private Mono<Builder> sendMailViaOutbox(CreationMessageEntry entry, MailboxSession session) {
-        if (!entry.getValue().isValid()) {
-            return Mono.error(new MailboxInvalidMessageCreationException());
-        }
-        return handleOutboxMessages(entry, session)
-                .map(created -> SetMessagesResponse.builder().created(created.getCreationId(), created.getValue()));
-    }
-
-    private Mono<Builder> saveDraft(CreationMessageEntry entry, MailboxSession session) {
-        return handleDraftMessages(entry, session)
-                .map(created -> SetMessagesResponse.builder().created(created.getCreationId(), created.getValue()));
-    }
-
-    @VisibleForTesting Mono<Void> assertIsUserOwnerOfMailboxes(List<MailboxId> mailboxIds, MailboxSession session) {
-        return allMailboxOwned(mailboxIds, session)
-            .handle((allOwned, sink) -> {
-                if (!allOwned) {
-                    sink.error(new MailboxNotOwnedException());
-                }
-            });
-    }
-
-    private Mono<Boolean> allMailboxOwned(List<MailboxId> mailboxIds, MailboxSession session) {
-        return Flux.fromIterable(mailboxIds)
-            .concatMap(id ->  mailboxManager.getMailboxReactive(id, session))
-            .map(Throwing.function(MessageManager::getMailboxPath))
-            .all(path -> path.belongsTo(session));
-    }
-
-    private Mono<MessageWithId> handleOutboxMessages(CreationMessageEntry entry, MailboxSession session) {
-        return assertUserCanSendFrom(session.getUser(), entry.getValue().getFrom())
-            .then(messageAppender.appendMessageInMailboxes(entry, toMailboxIds(entry), session))
-            .flatMap(newMessage ->
-                messageFullViewFactory.fromMetaDataWithContent(newMessage)
-                    .flatMap(Throwing.function((MessageFullView jmapMessage) -> {
-                        Envelope envelope = EnvelopeUtils.fromMessage(jmapMessage);
-                        return messageSender.sendMessage(newMessage, envelope, session)
-                            .then(referenceUpdater.updateReferences(entry.getValue().getHeaders(), session))
-                            .thenReturn(new ValueWithId.MessageWithId(entry.getCreationId(), jmapMessage));
-                    }).sneakyThrow()));
-    }
-
-    @VisibleForTesting
-    Mono<Void> assertUserCanSendFrom(Username connectedUser, Optional<DraftEmailer> from) {
-        Optional<Username> maybeFromUser = from.flatMap(DraftEmailer::getEmail)
-            .map(Username::of);
-
-        return Mono.from(canSendMailUsingIdentity(connectedUser, maybeFromUser))
-            .filter(Boolean::booleanValue)
-            .doOnNext(bool -> LOG.debug("{} is allowed to send a mail using {} identity", connectedUser.asString(), from))
-            .switchIfEmpty(Mono.error(() -> new MailboxSendingNotAllowedException(connectedUser, maybeFromUser)))
-            .then();
-    }
-
-    private Mono<Boolean> canSendMailUsingIdentity(Username connectedUser, Optional<Username> maybeFromUser) {
-        return Mono.justOrEmpty(maybeFromUser)
-            .flatMap(fromUser -> Mono.from(canSendFrom.userCanSendFromReactive(connectedUser, fromUser)));
-    }
-
-    private Mono<MessageWithId> handleDraftMessages(CreationMessageEntry entry, MailboxSession session) {
-        return messageAppender.appendMessageInMailboxes(entry, toMailboxIds(entry), session)
-            .flatMap(messageFullViewFactory::fromMetaDataWithContent)
-            .map(jmapMessage -> new ValueWithId.MessageWithId(entry.getCreationId(), jmapMessage));
-    }
-
-    private Mono<Boolean> isAppendToMailboxWithRole(Role role, CreationMessage entry, MailboxSession mailboxSession) {
-        return getMailboxWithRole(mailboxSession, role)
-            .map(entry::isOnlyIn)
-            .switchIfEmpty(Mono.just(false));
-    }
-
-    private Mono<Boolean> isTargettingAMailboxWithRole(Role role, CreationMessage entry, MailboxSession mailboxSession) {
-        return getMailboxWithRole(mailboxSession, role)
-            .map(entry::isIn)
-            .switchIfEmpty(Mono.just(false));
-    }
-
-    private Mono<MessageManager> getMailboxWithRole(MailboxSession mailboxSession, Role role) {
-        return Flux.from(systemMailboxesProvider.getMailboxByRole(role, mailboxSession.getUser()))
-            .next();
-    }
-
-    private SetError buildSetErrorFromValidationResult(List<ValidationResult> validationErrors) {
-        return SetError.builder()
-                .type(SetError.Type.INVALID_PROPERTIES)
-                .properties(collectMessageProperties(validationErrors))
-                .description(formatValidationErrorMessge(validationErrors))
-                .build();
-    }
-
-    private String formatValidationErrorMessge(List<ValidationResult> validationErrors) {
-        return validationErrors.stream()
-                .map(err -> err.getProperty() + ": " + err.getErrorMessage())
-                .collect(Collectors.joining("\\n"));
-    }
-
-    private Set<MessageProperties.MessageProperty> collectMessageProperties(List<ValidationResult> validationErrors) {
-        Splitter propertiesSplitter = Splitter.on(',').trimResults().omitEmptyStrings();
-        return validationErrors.stream()
-                .flatMap(err -> propertiesSplitter.splitToStream(err.getProperty()))
-                .flatMap(MessageProperty::find)
-                .collect(Collectors.toSet());
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java
deleted file mode 100644
index 3ad45be..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.util.List;
-
-import jakarta.inject.Inject;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.metrics.api.MetricFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import reactor.core.publisher.Mono;
-
-public class SetMessagesDestructionProcessor implements SetMessagesProcessor {
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMessagesCreationProcessor.class);
-
-    private final MessageIdManager messageIdManager;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    SetMessagesDestructionProcessor(MessageIdManager messageIdManager, MetricFactory metricFactory) {
-        this.messageIdManager = messageIdManager;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Mono<SetMessagesResponse> processReactive(SetMessagesRequest request, MailboxSession mailboxSession) {
-        if (request.getDestroy().isEmpty()) {
-            return Mono.just(SetMessagesResponse.builder().build());
-        }
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + "SetMessageDestructionProcessor",
-            delete(request.getDestroy(), mailboxSession)));
-    }
-
-    private Mono<SetMessagesResponse> delete(List<MessageId> toBeDestroyed, MailboxSession mailboxSession) {
-        if (toBeDestroyed.isEmpty()) {
-            return Mono.just(SetMessagesResponse.builder().build());
-        }
-        return Mono.from(messageIdManager.delete(ImmutableSet.copyOf(toBeDestroyed), mailboxSession))
-            .map(deleteResult -> SetMessagesResponse.builder()
-                .destroyed(ImmutableList.copyOf(deleteResult.getDestroyed()))
-                .notDestroyed(deleteResult.getNotFound().stream()
-                    .map(messageId -> Pair.of(messageId,
-                        SetError.builder()
-                            .type(SetError.Type.NOT_FOUND)
-                            .description("The message " + messageId.serialize() + " can't be found")
-                            .build()))
-                    .collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue)))
-                .build())
-            .onErrorResume(e -> {
-                LOGGER.error("An error occurred when deleting a message", e);
-                return Mono.just(
-                    SetMessagesResponse.builder()
-                        .notDestroyed(toBeDestroyed.stream()
-                            .map(messageId -> Pair.of(messageId,
-                                SetError.builder()
-                                    .type(SetError.Type.ERROR)
-                                    .description("An error occurred while deleting messages " + messageId.serialize())
-                                    .build()))
-                            .collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue)))
-                        .build());
-            });
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java
deleted file mode 100644
index 7cda49c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.util.MDCBuilder.ACTION;
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.Set;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.util.MDCBuilder;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetMessagesMethod implements Method {
-    private static final Method.Request.Name METHOD_NAME = Method.Request.name("setMessages");
-    private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("messagesSet");
-
-    private final Set<SetMessagesProcessor> messagesProcessors;
-
-    @Inject
-    @VisibleForTesting SetMessagesMethod(Set<SetMessagesProcessor> messagesProcessors) {
-        this.messagesProcessors = messagesProcessors;
-    }
-
-    @Override
-    public Method.Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return SetMessagesRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkArgument(request instanceof SetMessagesRequest);
-        SetMessagesRequest setMessagesRequest = (SetMessagesRequest) request;
-
-        return setMessagesResponse(setMessagesRequest, mailboxSession)
-            .map(responses -> JmapResponse.builder().methodCallId(methodCallId)
-                .response(responses)
-                .responseName(RESPONSE_NAME)
-                .build())
-            .flux()
-            .contextWrite(context(ACTION, mdc(setMessagesRequest)));
-    }
-
-    private MDCBuilder mdc(SetMessagesRequest setMessagesRequest) {
-        return MDCBuilder.create()
-            .addToContext(ACTION, "SET_MESSAGES")
-            .addToContextIfPresent("accountId", setMessagesRequest.getAccountId())
-            .addToContext("create", setMessagesRequest.getCreate().toString())
-            .addToContext("destroy", setMessagesRequest.getDestroy().toString())
-            .addToContextIfPresent("ifInState", setMessagesRequest.getIfInState());
-    }
-
-    private Mono<SetMessagesResponse> setMessagesResponse(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return Flux.fromIterable(messagesProcessors)
-            .flatMap(processor -> processor.processReactive(request, mailboxSession))
-            .reduce(SetMessagesResponse.builder(),
-                (builder, resp) -> resp.mergeInto(builder))
-            .map(SetMessagesResponse.Builder::build);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesProcessor.java
deleted file mode 100644
index e0fd37c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesProcessor.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.util.ReactorUtils;
-
-import reactor.core.publisher.Mono;
-
-public interface SetMessagesProcessor {
-    default SetMessagesResponse process(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return processReactive(request, mailboxSession).block();
-    }
-
-    default Mono<SetMessagesResponse> processReactive(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return Mono.fromCallable(() -> process(request, mailboxSession))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java
deleted file mode 100644
index 2580362..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import jakarta.inject.Inject;
-import jakarta.mail.Flags;
-import jakarta.mail.MessagingException;
-import jakarta.mail.Session;
-import jakarta.mail.internet.MimeMessage;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.InvalidOutboxMoveException;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.jmap.utils.KeywordsCombiner;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageManager.FlagsUpdateMode;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SystemMailboxesProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.OverQuotaException;
-import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
-import org.apache.james.mailbox.model.FetchGroup;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxId.Factory;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.MessageMoves;
-import org.apache.james.mailbox.model.MessageRange;
-import org.apache.james.mailbox.model.MessageResult;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.rrt.api.CanSendFrom;
-import org.apache.james.server.core.MailImpl;
-import org.apache.james.util.ReactorUtils;
-import org.apache.james.util.StreamUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMessagesUpdateProcessor.class);
-
-    private final UpdateMessagePatchConverter updatePatchConverter;
-    private final MessageIdManager messageIdManager;
-    private final MailboxManager mailboxManager;
-    private final SystemMailboxesProvider systemMailboxesProvider;
-    private final Factory mailboxIdFactory;
-    private final MetricFactory metricFactory;
-    private final MessageSender messageSender;
-
-    private final ReferenceUpdater referenceUpdater;
-    private final CanSendFrom canSendFrom;
-
-    @Inject
-    @VisibleForTesting SetMessagesUpdateProcessor(
-            UpdateMessagePatchConverter updatePatchConverter,
-            MessageIdManager messageIdManager,
-            MailboxManager mailboxManager,
-            SystemMailboxesProvider systemMailboxesProvider,
-            Factory mailboxIdFactory,
-            MessageSender messageSender,
-            MetricFactory metricFactory,
-            ReferenceUpdater referenceUpdater,
-            CanSendFrom canSendFrom) {
-        this.updatePatchConverter = updatePatchConverter;
-        this.messageIdManager = messageIdManager;
-        this.mailboxManager = mailboxManager;
-        this.systemMailboxesProvider = systemMailboxesProvider;
-        this.mailboxIdFactory = mailboxIdFactory;
-        this.metricFactory = metricFactory;
-        this.messageSender = messageSender;
-        this.referenceUpdater = referenceUpdater;
-        this.canSendFrom = canSendFrom;
-    }
-
-    @Override
-    public Mono<SetMessagesResponse> processReactive(SetMessagesRequest request, MailboxSession mailboxSession) {
-        if (!request.hasUpdates()) {
-            return Mono.just(SetMessagesResponse.builder().build());
-        }
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + "SetMessagesUpdateProcessor",
-            listMailboxIdsForRole(mailboxSession, Role.OUTBOX)
-                .flatMap(outboxIds -> prepareResponse(request, mailboxSession, outboxIds).map(SetMessagesResponse.Builder::build))
-                .onErrorResume(e ->
-                    Mono.just(request.buildUpdatePatches(updatePatchConverter).entrySet().stream()
-                        .map(entry -> prepareResponseIfCantReadOutboxes(e, entry.getKey(), entry.getValue()))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder())
-                        .build()))));
-    }
-
-    private SetMessagesResponse.Builder prepareResponseIfCantReadOutboxes(Throwable e, MessageId id, UpdateMessagePatch patch) {
-        if (patch.isValid()) {
-            return handleMessageUpdateException(id, e);
-        } else {
-            return handleInvalidRequest(id, patch.getValidationErrors(), patch);
-        }
-    }
-
-    private Mono<SetMessagesResponse.Builder> prepareResponse(SetMessagesRequest request, MailboxSession mailboxSession, Set<MailboxId> outboxes) {
-        Map<MessageId, UpdateMessagePatch> patches = request.buildUpdatePatches(updatePatchConverter);
-
-        return Flux.from(messageIdManager.messagesMetadata(patches.keySet(), mailboxSession))
-            .collect(ImmutableListMultimap.toImmutableListMultimap(metaData -> metaData.getComposedMessageId().getMessageId(), Function.identity()))
-            .flatMap(messages -> {
-                if (isAMassiveFlagUpdate(patches, messages)) {
-                    return Mono.fromCallable(() -> applyRangedFlagUpdate(patches, messages, mailboxSession))
-                        .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-                } else if (isAMassiveMove(patches, messages)) {
-                    return Mono.fromCallable(() -> applyMove(patches, messages, mailboxSession))
-                        .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-                } else {
-                    return Flux.fromIterable(patches.entrySet())
-                        .flatMap(entry -> {
-                            if (entry.getValue().isValid()) {
-                                return update(outboxes, entry.getKey(), entry.getValue(), mailboxSession, messages);
-                            } else {
-                                return Mono.just(handleInvalidRequest(entry.getKey(), entry.getValue().getValidationErrors(), entry.getValue()));
-                            }
-
-                        }).reduce(SetMessagesResponse.Builder::mergeWith)
-                        .switchIfEmpty(Mono.just(SetMessagesResponse.builder()));
-                }
-            });
-    }
-
-    private boolean isAMassiveFlagUpdate(Map<MessageId, UpdateMessagePatch> patches, Multimap<MessageId, ComposedMessageIdWithMetaData> messages) {
-        // The same patch, that only represents a flag update, is applied to messages within a single mailbox
-        return StreamUtils.isSingleValued(patches.values().stream())
-            && StreamUtils.isSingleValued(messages.values().stream().map(metaData -> metaData.getComposedMessageId().getMailboxId()))
-            && patches.values().iterator().next().isOnlyAFlagUpdate()
-            && messages.size() > 3;
-    }
-
-    private boolean isAMassiveMove(Map<MessageId, UpdateMessagePatch> patches, Multimap<MessageId, ComposedMessageIdWithMetaData> messages) {
-        // The same patch, that only represents a flag update, is applied to messages within a single mailbox
-        return StreamUtils.isSingleValued(patches.values().stream())
-            && StreamUtils.isSingleValued(messages.values().stream().map(metaData -> metaData.getComposedMessageId().getMailboxId()))
-            && patches.values().iterator().next().isOnlyAMove()
-            && messages.size() > 3;
-    }
-
-    private SetMessagesResponse.Builder applyRangedFlagUpdate(Map<MessageId, UpdateMessagePatch> patches, Multimap<MessageId, ComposedMessageIdWithMetaData> messages, MailboxSession mailboxSession) {
-        MailboxId mailboxId = messages.values()
-            .iterator()
-            .next()
-            .getComposedMessageId()
-            .getMailboxId();
-        UpdateMessagePatch patch = patches.values().iterator().next();
-        List<MessageRange> uidRanges = MessageRange.toRanges(messages.values().stream().map(metaData -> metaData.getComposedMessageId().getUid())
-            .distinct()
-            .collect(ImmutableList.toImmutableList()));
-
-        if (patch.isValid()) {
-            return uidRanges.stream().map(range -> {
-                ImmutableList<MessageId> messageIds = messages.entries()
-                    .stream()
-                    .filter(entry -> range.includes(entry.getValue().getComposedMessageId().getUid()))
-                    .map(Map.Entry::getKey)
-                    .distinct()
-                    .collect(ImmutableList.toImmutableList());
-                try {
-                    mailboxManager.getMailbox(mailboxId, mailboxSession)
-                        .setFlags(patch.applyToState(new Flags()), FlagsUpdateMode.REPLACE, range, mailboxSession);
-                    return SetMessagesResponse.builder().updated(messageIds);
-                } catch (MailboxException e) {
-                    return messageIds.stream()
-                        .map(messageId -> handleMessageUpdateException(messageId, e))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder());
-                } catch (IllegalArgumentException e) {
-                    ValidationResult invalidPropertyKeywords = ValidationResult.builder()
-                        .property(MessageProperties.MessageProperty.keywords.asFieldName())
-                        .message(e.getMessage())
-                        .build();
-
-                    return messageIds.stream()
-                        .map(messageId -> handleInvalidRequest(messageId, ImmutableList.of(invalidPropertyKeywords), patch))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder());
-                }
-            }).reduce(SetMessagesResponse.Builder::mergeWith)
-                .orElse(SetMessagesResponse.builder());
-        } else {
-            return messages.keySet().stream()
-                .map(messageId -> handleInvalidRequest(messageId, patch.getValidationErrors(), patch))
-                .reduce(SetMessagesResponse.Builder::mergeWith)
-                .orElse(SetMessagesResponse.builder());
-        }
-    }
-
-    private SetMessagesResponse.Builder applyMove(Map<MessageId, UpdateMessagePatch> patches, Multimap<MessageId, ComposedMessageIdWithMetaData> messages, MailboxSession mailboxSession) {
-        MailboxId mailboxId = messages.values()
-            .iterator()
-            .next()
-            .getComposedMessageId()
-            .getMailboxId();
-        UpdateMessagePatch patch = patches.values().iterator().next();
-        List<MessageRange> uidRanges = MessageRange.toRanges(messages.values().stream().map(metaData -> metaData.getComposedMessageId().getUid())
-            .distinct()
-            .collect(ImmutableList.toImmutableList()));
-
-        if (patch.isValid()) {
-            return uidRanges.stream().map(range -> {
-                ImmutableList<MessageId> messageIds = messages.entries()
-                    .stream()
-                    .filter(entry -> range.includes(entry.getValue().getComposedMessageId().getUid()))
-                    .map(Map.Entry::getKey)
-                    .distinct()
-                    .collect(ImmutableList.toImmutableList());
-                try {
-                    MailboxId targetId = mailboxIdFactory.fromString(patch.getMailboxIds().get().iterator().next());
-                    mailboxManager.moveMessages(range, mailboxId, targetId, mailboxSession);
-                    return SetMessagesResponse.builder().updated(messageIds);
-                } catch (OverQuotaException e) {
-                    return messageIds.stream()
-                        .map(messageId -> handleMessageUpdateQuotaException(messageId, e))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder());
-                } catch (MailboxException e) {
-                    return messageIds.stream()
-                        .map(messageId -> handleMessageUpdateException(messageId, e))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder());
-                } catch (IllegalArgumentException e) {
-                    ValidationResult invalidPropertyKeywords = ValidationResult.builder()
-                        .property(MessageProperties.MessageProperty.keywords.asFieldName())
-                        .message(e.getMessage())
-                        .build();
-
-                    return messageIds.stream()
-                        .map(messageId -> handleInvalidRequest(messageId, ImmutableList.of(invalidPropertyKeywords), patch))
-                        .reduce(SetMessagesResponse.Builder::mergeWith)
-                        .orElse(SetMessagesResponse.builder());
-                }
-            }).reduce(SetMessagesResponse.Builder::mergeWith)
-                .orElse(SetMessagesResponse.builder());
-        } else {
-            return messages.keySet().stream()
-                .map(messageId -> handleInvalidRequest(messageId, patch.getValidationErrors(), patch))
-                .reduce(SetMessagesResponse.Builder::mergeWith)
-                .orElse(SetMessagesResponse.builder());
-        }
-    }
-
-    private Mono<SetMessagesResponse.Builder> update(Set<MailboxId> outboxes, MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, Multimap<MessageId, ComposedMessageIdWithMetaData> metadata) {
-        try {
-            List<ComposedMessageIdWithMetaData> messages = Optional.ofNullable(metadata.get(messageId))
-                .map(ImmutableList::copyOf)
-                .orElse(ImmutableList.of());
-            assertValidUpdate(messages, updateMessagePatch, outboxes);
-
-            if (messages.isEmpty()) {
-                return Mono.just(SetMessagesResponse.builder().mergeWith(addMessageIdNotFoundToResponse(messageId)));
-            } else {
-                return setInMailboxes(messageId, updateMessagePatch, mailboxSession)
-                    .then(Flux.fromIterable(messages)
-                        .flatMap(message -> updateFlags(messageId, updateMessagePatch, mailboxSession, message))
-                        .then())
-                    .then(Mono.just(SetMessagesResponse.builder().updated(ImmutableList.of(messageId))))
-                    .flatMap(builder -> sendMessageWhenOutboxInTargetMailboxIds(outboxes, messageId, updateMessagePatch, mailboxSession)
-                        .map(builder::mergeWith))
-                    .onErrorResume(OverQuotaException.class, e -> Mono.just(handleMessageUpdateQuotaException(messageId, e)))
-                    .onErrorResume(MailboxException.class, e -> Mono.just(handleMessageUpdateException(messageId, e)))
-                    .onErrorResume(IOException.class, e -> Mono.just(handleMessageUpdateException(messageId, e)))
-                    .onErrorResume(MessagingException.class, e -> Mono.just(handleMessageUpdateException(messageId, e)))
-                    .onErrorResume(IllegalArgumentException.class, e -> {
-                        ValidationResult invalidPropertyKeywords = ValidationResult.builder()
-                            .property(MessageProperties.MessageProperty.keywords.asFieldName())
-                            .message(e.getMessage())
-                            .build();
-
-                        return Mono.just(handleInvalidRequest(messageId, ImmutableList.of(invalidPropertyKeywords), updateMessagePatch));
-                    });
-            }
-        } catch (InvalidOutboxMoveException e) {
-            ValidationResult invalidPropertyMailboxIds = ValidationResult.builder()
-                .property(MessageProperties.MessageProperty.mailboxIds.asFieldName())
-                .message(e.getMessage())
-                .build();
-
-            return Mono.just(handleInvalidRequest(messageId, ImmutableList.of(invalidPropertyMailboxIds), updateMessagePatch));
-        }
-    }
-
-    private Mono<SetMessagesResponse.Builder> sendMessageWhenOutboxInTargetMailboxIds(Set<MailboxId> outboxes, MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession) {
-        if (isTargetingOutbox(outboxes, listTargetMailboxIds(updateMessagePatch))) {
-            return Mono.from(messageIdManager.getMessagesReactive(ImmutableList.of(messageId), FetchGroup.FULL_CONTENT, mailboxSession))
-                .flatMap(messageToSend -> Mono.fromCallable(() -> buildMailFromMessage(messageToSend))
-                    .flatMap(mail -> assertUserCanSendFrom(mailboxSession.getUser(), mail.getMaybeSender().asOptional().map(Username::fromMailAddress))
-                        .then(Mono.just(Pair.of(messageToSend, mail)))))
-                .flatMap(Throwing.<Pair<MessageResult, MailImpl>, Mono<SetMessagesResponse.Builder>>function(
-                    pair -> messageSender.sendMessage(messageId, pair.getRight(), mailboxSession)
-                        .then(referenceUpdater.updateReferences(pair.getKey().getHeaders(), mailboxSession))
-                        .thenReturn(SetMessagesResponse.builder())).sneakyThrow())
-                .switchIfEmpty(Mono.just(addMessageIdNotFoundToResponse(messageId)));
-        }
-        return Mono.just(SetMessagesResponse.builder());
-    }
-
-    @VisibleForTesting
-    Mono<Void> assertUserCanSendFrom(Username connectedUser, Optional<Username> maybeFromUser) {
-        return Mono.justOrEmpty(maybeFromUser)
-            .flatMap(fromUser -> Mono.from(canSendFrom.userCanSendFromReactive(connectedUser, fromUser)))
-            .filter(Boolean::booleanValue)
-            .doOnNext(bool -> LOGGER.debug("{} is allowed to send a mail using {} identity", connectedUser.asString(), maybeFromUser))
-            .switchIfEmpty(Mono.error(() -> new MailboxSendingNotAllowedException(connectedUser, maybeFromUser)))
-            .then();
-    }
-
-    private void assertValidUpdate(List<ComposedMessageIdWithMetaData> messagesToBeUpdated,
-                                   UpdateMessagePatch updateMessagePatch,
-                                   Set<MailboxId> outboxMailboxes) {
-        ImmutableList<MailboxId> previousMailboxes = messagesToBeUpdated.stream()
-            .map(metaData -> metaData.getComposedMessageId().getMailboxId())
-            .collect(ImmutableList.toImmutableList());
-        List<MailboxId> targetMailboxes = getTargetedMailboxes(previousMailboxes, updateMessagePatch);
-
-        boolean isDraft = messagesToBeUpdated.stream()
-            .map(ComposedMessageIdWithMetaData::getFlags)
-            .map(Keywords.lenientFactory()::fromFlags)
-            .reduce(new KeywordsCombiner())
-            .orElse(Keywords.DEFAULT_VALUE)
-            .contains(Keyword.DRAFT);
-
-        MessageMoves messageMoves = MessageMoves.builder()
-            .previousMailboxIds(previousMailboxes)
-            .targetMailboxIds(targetMailboxes)
-            .build();
-
-        boolean targetContainsOutbox = messageMoves.addedMailboxIds().stream().anyMatch(outboxMailboxes::contains);
-        boolean targetIsOnlyOutbox = targetMailboxes.stream().allMatch(outboxMailboxes::contains);
-
-        assertOutboxMoveTargetsOnlyOutBox(targetContainsOutbox, targetIsOnlyOutbox);
-        assertOutboxMoveOriginallyHasDraftKeywordSet(targetContainsOutbox, isDraft);
-    }
-
-    private void assertOutboxMoveTargetsOnlyOutBox(boolean targetContainsOutbox, boolean targetIsOnlyOutbox) {
-        if (targetContainsOutbox && !targetIsOnlyOutbox) {
-            throw new InvalidOutboxMoveException("When moving a message to Outbox, only Outboxes mailboxes should be targeted.");
-        }
-    }
-
-    private void assertOutboxMoveOriginallyHasDraftKeywordSet(boolean targetIsOutbox, boolean isDraft) {
-        if (targetIsOutbox && !isDraft) {
-            throw new InvalidOutboxMoveException("Only message with `$Draft` keyword can be moved to Outbox");
-        }
-    }
-
-    private List<MailboxId> getTargetedMailboxes(ImmutableList<MailboxId> previousMailboxes, UpdateMessagePatch updateMessagePatch) {
-        return updateMessagePatch.getMailboxIds()
-            .map(ids -> ids.stream().map(mailboxIdFactory::fromString).collect(ImmutableList.toImmutableList()))
-            .orElse(previousMailboxes);
-    }
-
-    private MailImpl buildMailFromMessage(MessageResult message) throws MessagingException, IOException, MailboxException {
-        return MailImpl.fromMimeMessage(message.getMessageId().serialize(),
-            new MimeMessage(
-                Session.getDefaultInstance(new Properties()),
-                message.getFullContent().getInputStream()));
-    }
-
-    private Set<MailboxId> listTargetMailboxIds(UpdateMessagePatch updateMessagePatch) {
-        return updateMessagePatch.getMailboxIds()
-            .stream()
-            .flatMap(Collection::stream)
-            .map(mailboxIdFactory::fromString)
-            .collect(ImmutableSet.toImmutableSet());
-    }
-
-    private boolean isTargetingOutbox(Set<MailboxId> outboxes, Set<MailboxId> targetMailboxIds) {
-        return targetMailboxIds.stream().anyMatch(outboxes::contains);
-    }
-
-    private Mono<Set<MailboxId>> listMailboxIdsForRole(MailboxSession session, Role role) {
-        return Flux.from(systemMailboxesProvider.getMailboxByRole(role, session.getUser()))
-            .map(MessageManager::getId)
-            .collect(ImmutableSet.toImmutableSet());
-    }
-
-    private Mono<Void> updateFlags(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, ComposedMessageIdWithMetaData message) {
-        if (!updateMessagePatch.isFlagsIdentity()) {
-            return Mono.from(messageIdManager.setFlagsReactive(
-                updateMessagePatch.applyToState(message.getFlags()),
-                FlagsUpdateMode.REPLACE, messageId, ImmutableList.of(message.getComposedMessageId().getMailboxId()), mailboxSession));
-        }
-        return Mono.empty();
-    }
-
-    private Mono<Void> setInMailboxes(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession) {
-        Optional<List<String>> serializedMailboxIds = updateMessagePatch.getMailboxIds();
-        if (serializedMailboxIds.isPresent()) {
-            List<MailboxId> mailboxIds = serializedMailboxIds.get()
-                .stream()
-                .map(mailboxIdFactory::fromString)
-                .collect(ImmutableList.toImmutableList());
-
-            return Mono.from(messageIdManager.setInMailboxesReactive(messageId, mailboxIds, mailboxSession));
-        }
-        return Mono.empty();
-    }
-
-    private SetMessagesResponse.Builder addMessageIdNotFoundToResponse(MessageId messageId) {
-        return SetMessagesResponse.builder().notUpdated(ImmutableMap.of(messageId,
-            SetError.builder()
-                .type(SetError.Type.NOT_FOUND)
-                .properties(ImmutableSet.of(MessageProperties.MessageProperty.id))
-                .description("message not found")
-                .build()));
-    }
-
-    private SetMessagesResponse.Builder handleMessageUpdateException(MessageId messageId,
-                                                                     Throwable e) {
-        LOGGER.error("An error occurred when updating a message", e);
-        return SetMessagesResponse.builder().notUpdated(ImmutableMap.of(messageId, SetError.builder()
-                .type(SetError.Type.ERROR)
-                .description("An error occurred when updating a message")
-                .build()));
-    }
-
-    private SetMessagesResponse.Builder handleMessageUpdateQuotaException(MessageId messageId,
-                                                                     Throwable e) {
-        LOGGER.error("A quota restriction error occurred when updating a message", e);
-        return SetMessagesResponse.builder().notUpdated(messageId,
-                SetError.builder()
-                        .type(SetError.Type.MAX_QUOTA_REACHED)
-                        .description(e.getMessage())
-                        .build());
-    }
-
-    private SetMessagesResponse.Builder handleInvalidRequest(MessageId messageId,
-                                      ImmutableList<ValidationResult> validationErrors, UpdateMessagePatch patch) {
-        LOGGER.warn("Invalid update request with patch {} for message #{}: {}", patch, messageId, validationErrors);
-
-        String formattedValidationErrorMessage = validationErrors.stream()
-                .map(err -> err.getProperty() + ": " + err.getErrorMessage())
-                .collect(Collectors.joining(", "));
-
-        Set<MessageProperties.MessageProperty> properties = validationErrors.stream()
-                .flatMap(err -> MessageProperties.MessageProperty.find(err.getProperty()))
-                .collect(Collectors.toSet());
-
-        return SetMessagesResponse.builder().notUpdated(ImmutableMap.of(messageId, SetError.builder()
-                .type(SetError.Type.INVALID_PROPERTIES)
-                .properties(properties)
-                .description(formattedValidationErrorMessage)
-                .build()));
-
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java
deleted file mode 100644
index 43f1a22..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.jmap.utils.AccountIdUtil.toVacationAccountId;
-import static org.apache.james.util.ReactorUtils.context;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.api.model.AccountId;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetVacationRequest;
-import org.apache.james.jmap.draft.model.SetVacationResponse;
-import org.apache.james.jmap.draft.model.VacationResponse;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.MDCBuilder;
-import org.apache.james.vacation.api.Vacation;
-import org.apache.james.vacation.api.VacationService;
-
-import com.google.common.base.Preconditions;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetVacationResponseMethod implements Method {
-
-    public static final Request.Name METHOD_NAME = Request.name("setVacationResponse");
-    public static final Response.Name RESPONSE_NAME = Response.name("vacationResponseSet");
-    public static final String INVALID_ARGUMENTS = "invalidArguments";
-    public static final String ERROR_MESSAGE_BASE = "There is one VacationResponse object per account, with id set to \\\"singleton\\\" and not to ";
-    public static final String INVALID_ARGUMENTS1 = "invalidArguments";
-    public static final String INVALID_ARGUMENT_DESCRIPTION = "update field should just contain one entry with key \"singleton\"";
-
-    private final VacationService vacationService;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    public SetVacationResponseMethod(VacationService vacationService, MetricFactory metricFactory) {
-        this.vacationService = vacationService;
-        this.metricFactory = metricFactory;
-    }
-
-    @Override
-    public Request.Name requestHandled() {
-        return METHOD_NAME;
-    }
-
-    @Override
-    public Class<? extends JmapRequest> requestType() {
-        return SetVacationRequest.class;
-    }
-
-    @Override
-    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(methodCallId);
-        Preconditions.checkNotNull(mailboxSession);
-        Preconditions.checkArgument(request instanceof SetVacationRequest);
-        SetVacationRequest setVacationRequest = (SetVacationRequest) request;
-
-        return Flux.from(metricFactory.decoratePublisherWithTimerMetric(JMAP_PREFIX + METHOD_NAME.getName(),
-            process(methodCallId, mailboxSession, setVacationRequest)
-                .contextWrite(jmapAction("SET_VACATION"))
-                .contextWrite(context("set-vacation", MDCBuilder.ofValue("update", setVacationRequest.getUpdate().toString())))));
-    }
-
-    private Flux<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, SetVacationRequest setVacationRequest) {
-        if (!setVacationRequest.isValid()) {
-            return Flux.just(JmapResponse
-                .builder()
-                .methodCallId(methodCallId)
-                .error(ErrorResponse.builder()
-                    .type(INVALID_ARGUMENTS1)
-                    .description(INVALID_ARGUMENT_DESCRIPTION)
-                    .build())
-                .build());
-        }
-
-        return process(methodCallId,
-            AccountId.fromUsername(mailboxSession.getUser()),
-            setVacationRequest.getUpdate().get(Vacation.ID));
-    }
-
-
-    private Flux<JmapResponse> process(MethodCallId methodCallId, AccountId accountId, VacationResponse vacationResponse) {
-        if (vacationResponse.isValid()) {
-            return vacationService.modifyVacation(toVacationAccountId(accountId), vacationResponse.getPatch())
-                .thenMany(Mono.just(JmapResponse.builder()
-                    .methodCallId(methodCallId)
-                    .responseName(RESPONSE_NAME)
-                    .response(SetVacationResponse.builder()
-                        .updatedId(Vacation.ID)
-                        .build())
-                    .build()));
-        } else {
-            return Flux.just(JmapResponse.builder()
-                .methodCallId(methodCallId)
-                .responseName(RESPONSE_NAME)
-                .response(SetVacationResponse.builder()
-                    .notUpdated(Vacation.ID,
-                        SetError.builder()
-                            .type(SetError.Type.INVALID_ARGUMENTS)
-                            .description(ERROR_MESSAGE_BASE + vacationResponse.getId())
-                            .build())
-                    .build())
-                .build());
-        }
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverter.java
deleted file mode 100644
index e3a1a6b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.IOException;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.annotations.VisibleForTesting;
-
-public class UpdateMessagePatchConverter {
-
-    private final ObjectMapper jsonParser;
-    private final UpdateMessagePatchValidator validator;
-
-    @Inject
-    @VisibleForTesting
-    UpdateMessagePatchConverter(ObjectMapper jsonParser, UpdateMessagePatchValidator validator) {
-        this.jsonParser = jsonParser;
-        this.validator = validator;
-    }
-
-    public UpdateMessagePatch fromJsonNode(ObjectNode updatePatchNode) {
-        if (updatePatchNode == null || updatePatchNode.isNull() || updatePatchNode.isMissingNode()) {
-            throw new IllegalArgumentException("updatePatchNode");
-        }
-        if (! validator.isValid(updatePatchNode)) {
-            return UpdateMessagePatch.builder()
-                    .validationResult(validator.validate(updatePatchNode))
-                    .build();
-        }
-        try {
-            return jsonParser.readerFor(UpdateMessagePatch.class).<UpdateMessagePatch>readValue(updatePatchNode);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidator.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidator.java
deleted file mode 100644
index 5c986b7..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.model.MessageProperties;
-
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableSet;
-
-public class UpdateMessagePatchValidator implements Validator<ObjectNode> {
-
-    private final ObjectMapper parser;
-
-    @Inject
-    @VisibleForTesting UpdateMessagePatchValidator(ObjectMapperFactory parserFactory) {
-        this.parser = parserFactory.forParsing();
-    }
-
-    @Override
-    public boolean isValid(ObjectNode patch) {
-        return validate(patch).isEmpty();
-    }
-
-    @Override
-    public Set<ValidationResult> validate(ObjectNode json) {
-        try {
-            parser.treeToValue(json, UpdateMessagePatch.class);
-        } catch (JsonMappingException e) {
-            return ImmutableSet.of(ValidationResult.builder()
-                    .property(firstFieldFrom(e.getPath()).orElse(ValidationResult.UNDEFINED_PROPERTY))
-                    .message(e.getMessage())
-                    .build());
-        } catch (IOException e) {
-            return ImmutableSet.of(ValidationResult.builder()
-                    .message(e.getMessage())
-                    .build());
-        }
-        return ImmutableSet.of();
-    }
-
-    private Optional<String> firstFieldFrom(List<JsonMappingException.Reference> references) {
-        if (references == null) {
-            throw new IllegalArgumentException("references");
-        }
-        return references.stream()
-                .map(JsonMappingException.Reference::getFieldName)
-                .map(MessageProperties.MessageProperty::valueOf)
-                .findFirst()
-                .map(MessageProperties.MessageProperty::asFieldName);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValidationResult.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValidationResult.java
deleted file mode 100644
index 44efdea..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValidationResult.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.util.Objects;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-
-public class ValidationResult {
-
-    public static final String UNDEFINED_PROPERTY = "__UNDEFINED__";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private String property;
-        private String errorMessage;
-
-        public Builder property(String property) {
-            this.property = property;
-            return this;
-        }
-
-        public Builder message(String message) {
-            this.errorMessage = message;
-            return this;
-        }
-
-        public ValidationResult build() {
-            return new ValidationResult(property, errorMessage);
-        }
-
-    }
-
-    private final String property;
-    private final String errorMessage;
-
-    @VisibleForTesting
-    ValidationResult(String property, String errorMessage) {
-        this.property = property;
-        this.errorMessage = errorMessage;
-    }
-
-    public String getProperty() {
-        return property;
-    }
-
-    public String getErrorMessage() {
-        return errorMessage;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof ValidationResult) {
-            ValidationResult otherEMailer = (ValidationResult) o;
-            return Objects.equals(property, otherEMailer.property)
-                    && Objects.equals(errorMessage, otherEMailer.errorMessage);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(property, errorMessage);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("property", property)
-                .add("errorMessage", errorMessage)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/Validator.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/Validator.java
deleted file mode 100644
index 5971893..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/Validator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import java.util.Set;
-
-public interface Validator<T> {
-    boolean isValid(T item);
-    
-    Set<ValidationResult> validate(T item);
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValueWithId.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValueWithId.java
deleted file mode 100644
index cd28641..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/ValueWithId.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessageId;
-import org.apache.james.jmap.draft.model.JmapMDN;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-
-import com.google.common.base.MoreObjects;
-
-public class ValueWithId<T> {
-
-    private CreationMessageId creationId;
-    private T value;
-
-    private ValueWithId(CreationMessageId creationId, T value) {
-        this.creationId = creationId;
-        this.value = value;
-    }
-
-    public CreationMessageId getCreationId() {
-        return creationId;
-    }
-
-    public T getValue() {
-        return value;
-    }
-
-    public static class CreationMessageEntry extends ValueWithId<CreationMessage> {
-        public CreationMessageEntry(CreationMessageId creationId, CreationMessage message) {
-            super(creationId, message);
-        }
-    }
-
-    public static class MDNCreationEntry extends ValueWithId<JmapMDN> {
-        public MDNCreationEntry(CreationMessageId creationId, JmapMDN mdn) {
-            super(creationId, mdn);
-        }
-    }
-
-    public static class ErrorWithId extends ValueWithId<SetError> {
-        public ErrorWithId(CreationMessageId creationId, SetError error) {
-            super(creationId, error);
-        }
-    }
-
-    public static class MessageWithId extends ValueWithId<MessageFullView> {
-        public MessageWithId(CreationMessageId creationId, MessageFullView message) {
-            super(creationId, message);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("creationId", creationId)
-            .add("value", value)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenRequest.java
deleted file mode 100644
index c16d491..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenRequest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.jmap.draft.exceptions.MalformedContinuationTokenException;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-
-@JsonDeserialize(builder = AccessTokenRequest.Builder.class)
-public class AccessTokenRequest {
-
-    public static final String UNIQUE_JSON_PATH = "/token";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private ContinuationToken token;
-        private String method;
-        private String password;
-
-        private Builder() {
-
-        }
-
-        public Builder token(String token) throws MalformedContinuationTokenException {
-            this.token = ContinuationToken.fromString(token);
-            return this;
-        }
-
-        public Builder method(String method) {
-            this.method = method;
-            return this;
-        }
-
-        public Builder password(String password) {
-            this.password = password;
-            return this;
-        }
-
-        public AccessTokenRequest build() {
-            return new AccessTokenRequest(token, method, password);
-        }
-    }
-
-    private final ContinuationToken token;
-    private final String method;
-    private final String password;
-
-    private AccessTokenRequest(ContinuationToken token, String method, String password) {
-        this.token = token;
-        this.method = method;
-        this.password = password;
-    }
-
-    public ContinuationToken getToken() {
-        return token;
-    }
-
-    public String getMethod() {
-        return method;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenResponse.java
deleted file mode 100644
index 2125379..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AccessTokenResponse.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.jmap.api.access.AccessToken;
-
-public class AccessTokenResponse {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private AccessToken accessToken;
-        private String api;
-        private String eventSource;
-        private String upload;
-        private String download;
-
-        private Builder() {
-
-        }
-
-        public Builder accessToken(AccessToken accessToken) {
-            this.accessToken = accessToken;
-            return this;
-        }
-
-        public Builder api(String api) {
-            this.api = api;
-            return this;
-        }
-
-        public Builder eventSource(String eventSource) {
-            this.eventSource = eventSource;
-            return this;
-        }
-
-        public Builder upload(String upload) {
-            this.upload = upload;
-            return this;
-        }
-
-        public Builder download(String download) {
-            this.download = download;
-            return this;
-        }
-
-        public AccessTokenResponse build() {
-            return new AccessTokenResponse(accessToken, api, eventSource, upload, download);
-        }
-    }
-
-    private final AccessToken accessToken;
-    private final String api;
-    private final String eventSource;
-    private final String upload;
-    private final String download;
-
-    private AccessTokenResponse(AccessToken accessToken, String api, String eventSource, String upload, String download) {
-        this.accessToken = accessToken;
-        this.api = api;
-        this.eventSource = eventSource;
-        this.upload = upload;
-        this.download = download;
-    }
-
-    public String getAccessToken() {
-        return accessToken.serialize();
-    }
-
-    public String getApi() {
-        return api;
-    }
-
-    public String getEventSource() {
-        return eventSource;
-    }
-
-    public String getUpload() {
-        return upload;
-    }
-
-    public String getDownload() {
-        return download;
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AttachmentAccessToken.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AttachmentAccessToken.java
deleted file mode 100644
index 55981c9..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AttachmentAccessToken.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.List;
-import java.util.Objects;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
-public class AttachmentAccessToken implements SignedExpiringToken {
-
-    public static final char SEPARATOR = '_';
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static AttachmentAccessToken from(String serializedAttachmentAccessToken, String blobId) {
-        Preconditions.checkArgument(!Strings.isNullOrEmpty(serializedAttachmentAccessToken), "'AttachmentAccessToken' is mandatory");
-        List<String> split = Splitter.on(SEPARATOR).splitToList(serializedAttachmentAccessToken);
-        Preconditions.checkArgument(split.size() >= 3, "Wrong 'AttachmentAccessToken'");
-
-        String username = Joiner.on(SEPARATOR)
-            .join(split.stream()
-                .limit(split.size() - 2)
-                .collect(ImmutableList.toImmutableList()));
-
-        String defaultValue = null;
-        return builder()
-                .blobId(blobId)
-                .username(username)
-                .expirationDate(ZonedDateTime.parse(Iterables.get(split, split.size() - 2, defaultValue)))
-                .signature(Iterables.get(split, split.size() - 1, defaultValue))
-                .build();
-    }
-
-    public static class Builder {
-        private String username;
-        private String blobId;
-        private ZonedDateTime expirationDate;
-        private String signature;
-
-        private Builder() {
-
-        }
-        
-        public Builder blobId(String blobId) {
-            this.blobId = blobId;
-            return this;
-        }
-
-        public Builder username(String username) {
-            this.username = username;
-            return this;
-        }
-
-        public Builder expirationDate(ZonedDateTime expirationDate) {
-            this.expirationDate = expirationDate;
-            return this;
-        }
-
-        public Builder signature(String signature) {
-            this.signature = signature.trim();
-            return this;
-        }
-
-        public AttachmentAccessToken build() {
-            Preconditions.checkNotNull(username);
-            Preconditions.checkNotNull(blobId);
-            Preconditions.checkArgument(! blobId.isEmpty());
-            Preconditions.checkNotNull(expirationDate);
-            Preconditions.checkNotNull(signature);
-            return new AttachmentAccessToken(username, blobId, expirationDate, signature);
-        }
-    }
-    
-    private final String username;
-    private final String blobId;
-    private final ZonedDateTime expirationDate;
-    private final String signature;
-
-    @VisibleForTesting
-    AttachmentAccessToken(String username, String blobId, ZonedDateTime expirationDate, String signature) {
-        this.username = username;
-        this.blobId = blobId;
-        this.expirationDate = expirationDate;
-        this.signature = signature;
-    }
-
-    public String getBlobId() {
-        return blobId;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    @Override
-    public ZonedDateTime getExpirationDate() {
-        return expirationDate;
-    }
-
-    @Override
-    public String getSignature() {
-        return signature;
-    }
-
-    public String serialize() {
-        return getPayload()
-            + SEPARATOR
-            + signature;
-    }
-    
-    @Override
-    public String getPayload() {
-        return username
-            + SEPARATOR
-            + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(expirationDate);
-    }
-    
-    @Override
-    public String getSignedContent() {
-        return blobId
-            + SEPARATOR
-            + getPayload();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other instanceof AttachmentAccessToken) {
-            AttachmentAccessToken attachmentAccessToken = (AttachmentAccessToken) other;
-            return Objects.equals(username, attachmentAccessToken.username)
-                    && Objects.equals(blobId, attachmentAccessToken.blobId)
-                    && Objects.equals(expirationDate, attachmentAccessToken.expirationDate)
-                    && Objects.equals(signature, attachmentAccessToken.signature);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(username, blobId, expirationDate, signature);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("username", username)
-                .add("blobId", blobId)
-                .add("expirationDate", expirationDate)
-                .add("signature", signature)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AuthenticatedRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AuthenticatedRequest.java
deleted file mode 100644
index 31a751f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/AuthenticatedRequest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.mailbox.MailboxSession;
-
-public class AuthenticatedRequest extends InvocationRequest {
-    public static AuthenticatedRequest decorate(InvocationRequest request, MailboxSession session) {
-        return new AuthenticatedRequest(request, session);
-    }
-
-    private final MailboxSession session;
-
-    private AuthenticatedRequest(InvocationRequest request, MailboxSession session) {
-        super(request.getMethodName(), request.getParameters(), request.getMethodCallId());
-        this.session = session;
-        
-    }
-
-    public MailboxSession getMailboxSession() {
-        return session;
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationToken.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationToken.java
deleted file mode 100644
index 4021092..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationToken.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.DateTimeException;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.LinkedList;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-
-import org.apache.james.jmap.draft.exceptions.MalformedContinuationTokenException;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-
-public class ContinuationToken implements SignedExpiringToken {
-
-    public static final String SEPARATOR = "_";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-    
-    public static class Builder {
-        private String username;
-        private ZonedDateTime expirationDate;
-        private String signature;
-
-        private Builder() {
-
-        }
-        
-        public Builder username(String username) {
-            this.username = username;
-            return this;
-        }
-
-        public Builder expirationDate(ZonedDateTime expirationDate) {
-            this.expirationDate = expirationDate;
-            return this;
-        }
-
-        public Builder signature(String signature) {
-            this.signature = signature;
-            return this;
-        }
-
-        public ContinuationToken build() {
-            return new ContinuationToken(username, expirationDate, signature);
-        }
-    }
-    
-    public static ContinuationToken fromString(String serializedToken) throws MalformedContinuationTokenException {
-        Preconditions.checkArgument(!Strings.isNullOrEmpty(serializedToken), "Serialized continuation token should not be null or empty");
-        LinkedList<String> tokenParts = Lists.newLinkedList(Splitter.on(SEPARATOR).split(serializedToken));
-        try {
-            return ContinuationToken.builder()
-                    .signature(tokenParts.removeLast())
-                    .expirationDate(ZonedDateTime.parse(tokenParts.removeLast(), DateTimeFormatter.ISO_OFFSET_DATE_TIME))
-                    .username(Joiner.on(SEPARATOR).join(tokenParts))
-                    .build();
-        } catch (NoSuchElementException | IllegalArgumentException e) {
-            throw new MalformedContinuationTokenException("Token " + serializedToken + " does not have enough parts", e);
-        } catch (DateTimeException e) {
-            throw new MalformedContinuationTokenException("Token " + serializedToken + " as an incorrect date", e);
-        }
-    }
-
-    private final String username;
-    private final ZonedDateTime expirationDate;
-    private final String signature;
-
-    public ContinuationToken(String username, ZonedDateTime expirationDate, String signature) {
-        Preconditions.checkNotNull(username);
-        Preconditions.checkArgument(! username.isEmpty());
-        Preconditions.checkNotNull(expirationDate);
-        Preconditions.checkNotNull(signature);
-        this.username = username;
-        this.expirationDate = expirationDate;
-        this.signature = signature;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    @Override
-    public ZonedDateTime getExpirationDate() {
-        return expirationDate;
-    }
-
-    @Override
-    public String getSignature() {
-        return signature;
-    }
-
-    public String serialize() {
-        return getPayload()
-            + SEPARATOR
-            + signature;
-    }
-    
-    @Override
-    public String getPayload() {
-        return username
-            + SEPARATOR
-            + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(expirationDate);
-    }
-
-    @Override
-    public String getSignedContent() {
-        return getPayload();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other == null || getClass() != other.getClass()) {
-            return false;
-        }
-        ContinuationToken continuationToken = (ContinuationToken) other;
-        return Objects.equals(username, continuationToken.username)
-            && expirationDate.isEqual(continuationToken.expirationDate)
-            && Objects.equals(signature, continuationToken.signature);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(username, expirationDate, signature);
-    }
-
-    @Override
-    public String toString() {
-        return "ContinuationToken{" +
-            "username='" + username + '\'' +
-            ", expirationDate=" + expirationDate +
-            ", signature='" + signature + '\'' +
-            '}';
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenRequest.java
deleted file mode 100644
index 2ee72a4..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenRequest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-
-@JsonDeserialize(builder = ContinuationTokenRequest.Builder.class)
-public class ContinuationTokenRequest {
-
-    public static final String UNIQUE_JSON_PATH = "/username";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private String username;
-        private String clientName;
-        private String clientVersion;
-        private String deviceName;
-
-        private Builder() {
-
-        }
-
-        public Builder username(String username) {
-            this.username = username;
-            return this;
-        }
-
-        public Builder clientName(String clientName) {
-            this.clientName = clientName;
-            return this;
-        }
-
-        public Builder clientVersion(String clientVersion) {
-            this.clientVersion = clientVersion;
-            return this;
-        }
-
-        public Builder deviceName(String deviceName) {
-            this.deviceName = deviceName;
-            return this;
-        }
-
-        public ContinuationTokenRequest build() {
-            return new ContinuationTokenRequest(username, clientName, clientVersion, deviceName);
-        }
-    }
-
-    private final String username;
-    private final String clientName;
-    private final String clientVersion;
-    private final String deviceName;
-
-    private ContinuationTokenRequest(String username, String clientName, String clientVersion, String deviceName) {
-        this.username = username;
-        this.clientName = clientName;
-        this.clientVersion = clientVersion;
-        this.deviceName = deviceName;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public String getClientName() {
-        return clientName;
-    }
-
-    public String getClientVersion() {
-        return clientVersion;
-    }
-
-    public String getDeviceName() {
-        return deviceName;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenResponse.java
deleted file mode 100644
index ea99b01..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/ContinuationTokenResponse.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.collect.ImmutableList;
-
-public class ContinuationTokenResponse {
-
-    public enum AuthenticationMethod {
-        PASSWORD("password"),
-        EXTERNAL("external"),
-        PROMPT("prompt");
-
-        private final String value;
-
-        AuthenticationMethod(String value) {
-            this.value = value;
-        }
-
-        @JsonValue
-        public String toString() {
-            return value;
-        }
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private String continuationToken;
-        private ImmutableList<AuthenticationMethod> methods;
-        private String prompt;
-
-        private Builder() {
-
-        }
-
-        public Builder continuationToken(ContinuationToken continuationToken) {
-            this.continuationToken = continuationToken.serialize();
-            return this;
-        }
-
-        public Builder methods(List<AuthenticationMethod> methods) {
-            this.methods = ImmutableList.copyOf(methods);
-            return this;
-        }
-
-        public Builder methods(AuthenticationMethod... methods) {
-            this.methods = ImmutableList.copyOf(methods);
-            return this;
-        }
-
-        public Builder prompt(String prompt) {
-            this.prompt = prompt;
-            return this;
-        }
-
-        public ContinuationTokenResponse build() {
-            return new ContinuationTokenResponse(continuationToken, methods, prompt);
-        }
-    }
-
-    private final String continuationToken;
-    private final ImmutableList<AuthenticationMethod> methods;
-    private final String prompt;
-
-    private ContinuationTokenResponse(String continuationToken, ImmutableList<AuthenticationMethod> methods, String prompt) {
-        this.continuationToken = continuationToken;
-        this.methods = methods;
-        this.prompt = prompt;
-    }
-
-    public String getContinuationToken() {
-        return continuationToken;
-    }
-
-    public List<AuthenticationMethod> getMethods() {
-        return methods;
-    }
-
-    public String getPrompt() {
-        return prompt;
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessage.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessage.java
deleted file mode 100644
index c32b8e6..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessage.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import jakarta.mail.internet.AddressException;
-import jakarta.mail.internet.InternetAddress;
-
-import org.apache.james.jmap.draft.methods.ValidationResult;
-import org.apache.james.jmap.model.Attachment;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.apache.james.jmap.model.message.view.SubMessage;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.util.StreamUtils;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-@JsonDeserialize(builder = CreationMessage.Builder.class)
-public class CreationMessage {
-
-    private static final String RECIPIENT_PROPERTY_NAMES = ImmutableList.of(MessageProperty.to, MessageProperty.cc, MessageProperty.bcc).stream()
-            .map(MessageProperty::asFieldName)
-            .collect(Collectors.joining(", "));
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private ImmutableList<String> mailboxIds;
-        private String inReplyToMessageId;
-        private final OldKeyword.Builder oldKeywordBuilder;
-        private final ImmutableMap.Builder<String, String> headers;
-        private Optional<DraftEmailer> from = Optional.empty();
-        private final ImmutableList.Builder<DraftEmailer> to;
-        private final ImmutableList.Builder<DraftEmailer> cc;
-        private final ImmutableList.Builder<DraftEmailer> bcc;
-        private final ImmutableList.Builder<DraftEmailer> replyTo;
-        private String subject;
-        private ZonedDateTime date;
-        private String textBody;
-        private String htmlBody;
-        private final ImmutableList.Builder<Attachment> attachments;
-        private final ImmutableMap.Builder<BlobId, SubMessage> attachedMessages;
-        private Optional<Map<String, Boolean>> keywords = Optional.empty();
-
-        private Builder() {
-            to = ImmutableList.builder();
-            cc = ImmutableList.builder();
-            bcc = ImmutableList.builder();
-            replyTo = ImmutableList.builder();
-            attachments = ImmutableList.builder();
-            attachedMessages = ImmutableMap.builder();
-            headers = ImmutableMap.builder();
-            oldKeywordBuilder = OldKeyword.builder();
-        }
-
-        public Builder mailboxId(String... mailboxIds) {
-            return mailboxIds(Arrays.asList(mailboxIds));
-        }
-
-        @JsonDeserialize
-        public Builder mailboxIds(List<String> mailboxIds) {
-            this.mailboxIds = ImmutableList.copyOf(mailboxIds);
-            return this;
-        }
-
-        public Builder inReplyToMessageId(String inReplyToMessageId) {
-            this.inReplyToMessageId = inReplyToMessageId;
-            return this;
-        }
-
-        public Builder isUnread(Optional<Boolean> isUnread) {
-            oldKeywordBuilder.isUnread(isUnread);
-            return this;
-        }
-
-        public Builder isFlagged(Optional<Boolean> isFlagged) {
-            oldKeywordBuilder.isFlagged(isFlagged);
-            return this;
-        }
-
-        public Builder isAnswered(Optional<Boolean> isAnswered) {
-            oldKeywordBuilder.isAnswered(isAnswered);
-            return this;
-        }
-
-        public Builder isDraft(Optional<Boolean> isDraft) {
-            oldKeywordBuilder.isDraft(isDraft);
-            return this;
-        }
-
-        public Builder isForwarded(Optional<Boolean> isForwarded) {
-            oldKeywordBuilder.isForwarded(isForwarded);
-            return this;
-        }
-
-        public Builder headers(ImmutableMap<String, String> headers) {
-            this.headers.putAll(headers);
-            return this;
-        }
-
-        public Builder from(DraftEmailer from) {
-            this.from = Optional.ofNullable(from);
-            return this;
-        }
-
-        public Builder to(List<DraftEmailer> to) {
-            this.to.addAll(to);
-            return this;
-        }
-
-        public Builder cc(List<DraftEmailer> cc) {
-            this.cc.addAll(cc);
-            return this;
-        }
-
-        public Builder bcc(List<DraftEmailer> bcc) {
-            this.bcc.addAll(bcc);
-            return this;
-        }
-
-        public Builder replyTo(List<DraftEmailer> replyTo) {
-            this.replyTo.addAll(replyTo);
-            return this;
-        }
-
-        public Builder subject(String subject) {
-            this.subject = Strings.nullToEmpty(subject);
-            return this;
-        }
-
-        public Builder date(ZonedDateTime date) {
-            this.date = date;
-            return this;
-        }
-
-        public Builder textBody(String textBody) {
-            this.textBody = textBody;
-            return this;
-        }
-
-        public Builder htmlBody(String htmlBody) {
-            this.htmlBody = htmlBody;
-            return this;
-        }
-
-        public Builder attachments(Attachment... attachments) {
-            return attachments(Arrays.asList(attachments));
-        }
-        
-        @JsonDeserialize
-        public Builder attachments(List<Attachment> attachments) {
-            this.attachments.addAll(attachments);
-            return this;
-        }
-
-        public Builder attachedMessages(Map<BlobId, SubMessage> attachedMessages) {
-            this.attachedMessages.putAll(attachedMessages);
-            return this;
-        }
-
-        public Builder keywords(Map<String, Boolean> keywords) {
-            this.keywords = Optional.of(ImmutableMap.copyOf(keywords));
-            return this;
-        }
-
-        private static boolean areAttachedMessagesKeysInAttachments(ImmutableList<Attachment> attachments, ImmutableMap<BlobId, SubMessage> attachedMessages) {
-            return attachedMessages.isEmpty() || attachedMessages.keySet().stream()
-                    .anyMatch(inAttachments(attachments));
-        }
-
-        private static Predicate<BlobId> inAttachments(ImmutableList<Attachment> attachments) {
-            return (key) -> attachments.stream()
-                .map(Attachment::getBlobId)
-                .anyMatch(blobId -> blobId.equals(key));
-        }
-
-        public CreationMessage build() {
-            Preconditions.checkState(mailboxIds != null, "'mailboxIds' is mandatory");
-            Preconditions.checkState(headers != null, "'headers' is mandatory");
-            ImmutableList<Attachment> attachments = this.attachments.build();
-            ImmutableMap<BlobId, SubMessage> attachedMessages = this.attachedMessages.build();
-            Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments, attachedMessages), "'attachedMessages' keys must be in 'attachments'");
-
-            if (date == null) {
-                date = ZonedDateTime.now();
-            }
-
-            Optional<Keywords> maybeKeywords = creationKeywords();
-            Optional<OldKeyword> oldKeywords = oldKeywordBuilder.computeOldKeyword();
-
-            return new CreationMessage(mailboxIds, Optional.ofNullable(inReplyToMessageId), headers.build(), from,
-                    to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, Optional.ofNullable(textBody), Optional.ofNullable(htmlBody),
-                    attachments, attachedMessages, computeKeywords(maybeKeywords, oldKeywords));
-        }
-
-        public Optional<Keywords> creationKeywords() {
-            return keywords.map(map -> Keywords.strictFactory()
-                    .fromMap(map));
-        }
-
-        public Keywords computeKeywords(Optional<Keywords> keywords, Optional<OldKeyword> oldKeywords) {
-            Preconditions.checkArgument(!(keywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time");
-            return keywords
-                .or(() -> oldKeywords.map(OldKeyword::asKeywords))
-                .orElse(Keywords.DEFAULT_VALUE);
-        }
-
-    }
-
-    private final ImmutableList<String> mailboxIds;
-    private final Optional<String> inReplyToMessageId;
-    private final ImmutableMap<String, String> headers;
-    private final Optional<DraftEmailer> from;
-    private final ImmutableList<DraftEmailer> to;
-    private final ImmutableList<DraftEmailer> cc;
-    private final ImmutableList<DraftEmailer> bcc;
-    private final ImmutableList<DraftEmailer> replyTo;
-    private final String subject;
-    private final ZonedDateTime date;
-    private final Optional<String> textBody;
-    private final Optional<String> htmlBody;
-    private final ImmutableList<Attachment> attachments;
-    private final ImmutableMap<BlobId, SubMessage> attachedMessages;
-    private final Keywords keywords;
-
-    @VisibleForTesting
-    CreationMessage(ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, ImmutableMap<String, String> headers, Optional<DraftEmailer> from,
-                    ImmutableList<DraftEmailer> to, ImmutableList<DraftEmailer> cc, ImmutableList<DraftEmailer> bcc, ImmutableList<DraftEmailer> replyTo, String subject, ZonedDateTime date, Optional<String> textBody, Optional<String> htmlBody, ImmutableList<Attachment> attachments,
-                    ImmutableMap<BlobId, SubMessage> attachedMessages, Keywords keywords) {
-        this.mailboxIds = mailboxIds;
-        this.inReplyToMessageId = inReplyToMessageId;
-        this.headers = headers;
-        this.from = from;
-        this.to = to;
-        this.cc = cc;
-        this.bcc = bcc;
-        this.replyTo = replyTo;
-        this.subject = subject;
-        this.date = date;
-        this.textBody = textBody;
-        this.htmlBody = htmlBody;
-        this.attachments = attachments;
-        this.attachedMessages = attachedMessages;
-        this.keywords = keywords;
-    }
-
-    public Keywords getKeywords() {
-        return keywords;
-    }
-
-    public ImmutableList<String> getMailboxIds() {
-        return mailboxIds;
-    }
-
-    public Optional<String> getInReplyToMessageId() {
-        return inReplyToMessageId;
-    }
-
-    public ImmutableMap<String, String> getHeaders() {
-        return headers;
-    }
-
-    public Optional<DraftEmailer> getFrom() {
-        return from;
-    }
-
-    public ImmutableList<DraftEmailer> getTo() {
-        return to;
-    }
-
-    public ImmutableList<DraftEmailer> getCc() {
-        return cc;
-    }
-
-    public ImmutableList<DraftEmailer> getBcc() {
-        return bcc;
-    }
-
-    public ImmutableList<DraftEmailer> getReplyTo() {
-        return replyTo;
-    }
-
-    public String getSubject() {
-        return subject;
-    }
-
-    public ZonedDateTime getDate() {
-        return date;
-    }
-
-    public Optional<String> getTextBody() {
-        return textBody;
-    }
-
-    public Optional<String> getHtmlBody() {
-        return htmlBody;
-    }
-
-    public ImmutableList<Attachment> getAttachments() {
-        return attachments;
-    }
-
-    public ImmutableMap<BlobId, SubMessage> getAttachedMessages() {
-        return attachedMessages;
-    }
-
-    public boolean isValid() {
-        return validate().isEmpty();
-    }
-
-    public boolean isDraft() {
-        return keywords.contains(Keyword.DRAFT);
-    }
-
-    public List<ValidationResult> validate() {
-        ImmutableList.Builder<ValidationResult> errors = ImmutableList.builder();
-        assertValidFromProvided(errors);
-        assertAtLeastOneValidRecipient(errors);
-        return errors.build();
-    }
-
-    private void assertAtLeastOneValidRecipient(ImmutableList.Builder<ValidationResult> errors) {
-        Stream<DraftEmailer> recipients = StreamUtils.flatten(to.stream(), cc.stream(), bcc.stream());
-        boolean hasAtLeastOneAddressToSendTo = recipients.anyMatch(DraftEmailer::hasValidEmail);
-        if (!hasAtLeastOneAddressToSendTo) {
-            errors.add(ValidationResult.builder().message("no recipient address set").property(RECIPIENT_PROPERTY_NAMES).build());
-        }
-    }
-
-    private void assertValidFromProvided(ImmutableList.Builder<ValidationResult> errors) {
-        ValidationResult invalidPropertyFrom = ValidationResult.builder()
-                .property(MessageProperty.from.asFieldName())
-                .message("'from' address is mandatory")
-                .build();
-        if (!from.isPresent()) {
-            errors.add(invalidPropertyFrom);
-        }
-        from.filter(f -> !f.hasValidEmail()).ifPresent(f -> errors.add(invalidPropertyFrom));
-    }
-
-    public boolean isIn(MessageManager mailbox) {
-        return mailboxIds.contains(mailbox.getId().serialize());
-    }
-
-    public boolean isOnlyIn(MessageManager mailbox) {
-        return isIn(mailbox)
-            && mailboxIds.size() == 1;
-    }
-    
-    @JsonDeserialize(builder = DraftEmailer.Builder.class)
-    public static class DraftEmailer {
-
-        public static Builder builder() {
-            return new Builder();
-        }
-
-        @JsonPOJOBuilder(withPrefix = "")
-        public static class Builder {
-            private Optional<String> name = Optional.empty();
-            private Optional<String> email = Optional.empty();
-
-            public Builder name(String name) {
-                this.name = Optional.ofNullable(name);
-                return this;
-            }
-
-            public Builder email(String email) {
-                this.email = Optional.ofNullable(email);
-                return this;
-            }
-
-            public DraftEmailer build() {
-                return new DraftEmailer(name, email);
-            }
-        }
-
-        private final Optional<String> name;
-        private final Optional<String> email;
-        private final EmailValidator emailValidator;
-
-        @VisibleForTesting
-        DraftEmailer(Optional<String> name, Optional<String> email) {
-            this.name = name;
-            this.email = email;
-            this.emailValidator = new EmailValidator();
-        }
-
-        public Optional<String> getName() {
-            return name;
-        }
-
-        public Optional<String> getEmail() {
-            return email;
-        }
-
-        public boolean hasValidEmail() {
-            return getEmail().isPresent() && emailValidator.isValid(getEmail().get());
-        }
-
-        public EmailUserAndDomain getEmailUserAndDomain() {
-            int atIndex = email.get().indexOf('@');
-            if (atIndex < 0 || atIndex == email.get().length() - 1) {
-                return new EmailUserAndDomain(Optional.of(email.get()), Optional.empty());
-            }
-            String user = email.get().substring(0, atIndex);
-            String domain = email.get().substring(atIndex + 1);
-
-            return new EmailUserAndDomain(Optional.of(user), Optional.of(domain));
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof DraftEmailer) {
-                DraftEmailer otherEMailer = (DraftEmailer) o;
-                return Objects.equals(name, otherEMailer.name)
-                        && Objects.equals(email.orElse("<unset>"), otherEMailer.email.orElse("<unset>"));
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(name, email);
-        }
-
-        @Override
-        public String toString() {
-            return MoreObjects.toStringHelper(this)
-                    .add("name", name)
-                    .add("email", email.orElse("<unset>"))
-                    .toString();
-        }
-    }
-
-    public static class EmailUserAndDomain {
-        private final Optional<String> user;
-        private final Optional<String> domain;
-
-        public EmailUserAndDomain(Optional<String> user, Optional<String> domain) {
-            this.user = user;
-            this.domain = domain;
-        }
-
-        public Optional<String> getUser() {
-            return user;
-        }
-
-        public Optional<String> getDomain() {
-            return domain;
-        }
-    }
-
-    public static class EmailValidator {
-
-        public boolean isValid(String email) {
-            boolean result = true;
-            try {
-                InternetAddress emailAddress = new InternetAddress(email);
-                // verrrry permissive validator !
-                emailAddress.validate();
-            } catch (AddressException ex) {
-                result = false;
-            }
-            return result;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("mailboxIds", mailboxIds)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessageId.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessageId.java
deleted file mode 100644
index 9c72d9a..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/CreationMessageId.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Objects;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-public class CreationMessageId {
-
-    public static CreationMessageId of(String creationMessageId) {
-        return new CreationMessageId(creationMessageId);
-    }
-
-    private final String id;
-
-    private CreationMessageId(String id) {
-        Preconditions.checkNotNull(id);
-        Preconditions.checkArgument(!id.isEmpty());
-        this.id = id;
-    }
-
-    @JsonValue
-    public String getId() {
-        return id;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof CreationMessageId) {
-            CreationMessageId other = (CreationMessageId) obj;
-            return Objects.equals(this.id, other.id);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("id", id)
-            .toString();
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EndPointsResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EndPointsResponse.java
deleted file mode 100644
index ecd86db..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EndPointsResponse.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-public class EndPointsResponse {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private String api;
-        private String eventSource;
-        private String upload;
-        private String download;
-
-        private Builder() {
-        }
-
-        public Builder api(String api) {
-            this.api = api;
-            return this;
-        }
-
-        public Builder eventSource(String eventSource) {
-            this.eventSource = eventSource;
-            return this;
-        }
-
-        public Builder upload(String upload) {
-            this.upload = upload;
-            return this;
-        }
-
-        public Builder download(String download) {
-            this.download = download;
-            return this;
-        }
-
-        public EndPointsResponse build() {
-            return new EndPointsResponse(api, eventSource, upload, download);
-        }
-    }
-
-    private final String api;
-    private final String eventSource;
-    private final String upload;
-    private final String download;
-
-    private EndPointsResponse(String api, String eventSource, String upload, String download) {
-        this.api = api;
-        this.eventSource = eventSource;
-        this.upload = upload;
-        this.download = download;
-    }
-
-    public String getApi() {
-        return api;
-    }
-
-    public String getEventSource() {
-        return eventSource;
-    }
-
-    public String getUpload() {
-        return upload;
-    }
-
-    public String getDownload() {
-        return download;
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EnvelopeUtils.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EnvelopeUtils.java
deleted file mode 100644
index a3dac74..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/EnvelopeUtils.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.stream.Stream;
-
-import org.apache.james.core.MailAddress;
-import org.apache.james.core.MaybeSender;
-import org.apache.james.jmap.model.Emailer;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.server.core.Envelope;
-import org.apache.james.util.StreamUtils;
-
-import com.google.common.collect.ImmutableSet;
-
-public class EnvelopeUtils {
-    public static Envelope fromMessage(MessageFullView jmapMessage) {
-        MaybeSender sender = MaybeSender.of(jmapMessage.getFrom()
-            .map(Emailer::toMailAddress)
-            .orElseThrow(() -> new RuntimeException("Sender is mandatory")));
-
-        Stream<MailAddress> to = emailersToMailAddresses(jmapMessage.getTo());
-        Stream<MailAddress> cc = emailersToMailAddresses(jmapMessage.getCc());
-        Stream<MailAddress> bcc = emailersToMailAddresses(jmapMessage.getBcc());
-
-        return new Envelope(sender,
-            StreamUtils.flatten(Stream.of(to, cc, bcc))
-                .collect(ImmutableSet.toImmutableSet()));
-    }
-
-    private static Stream<MailAddress> emailersToMailAddresses(List<Emailer> emailers) {
-        return emailers.stream()
-            .map(Emailer::toMailAddress);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
deleted file mode 100644
index 153dd6f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.stream.Stream;
-
-import org.apache.james.jmap.draft.json.FilterDeserializer;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(using = FilterDeserializer.class)
-public interface Filter {
-    class TooDeepFilterHierarchyException extends IllegalArgumentException {
-        TooDeepFilterHierarchyException() {
-            super("Filter depth is higher than maximum allowed value " + MAX_FILTER_DEPTH);
-        }
-    }
-
-    int MAX_FILTER_DEPTH = 10;
-
-    String prettyPrint(String indentation);
-
-    default boolean inMailboxFilterOnly() {
-        return false;
-    }
-
-    default boolean inMailboxAndAfterFiltersOnly() {
-        return false;
-    }
-
-    default List<FilterCondition> breadthFirstVisit() {
-        return this.breadthFirstVisit(0)
-            .collect(ImmutableList.toImmutableList());
-    }
-
-    default Stream<FilterCondition> breadthFirstVisit(int depth) {
-        if (depth > MAX_FILTER_DEPTH) {
-            throw new TooDeepFilterHierarchyException();
-        }
-        if (this instanceof FilterOperator) {
-            FilterOperator operator = (FilterOperator) this;
-
-            return operator.getConditions().stream()
-                .flatMap(filter -> filter.breadthFirstVisit(depth + 1));
-        }
-        if (this instanceof FilterCondition) {
-            return Stream.of((FilterCondition) this);
-        }
-        throw new RuntimeException("Unsupported Filter implementation " + this);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
deleted file mode 100644
index 27d4fd2..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
+++ /dev/null
@@ -1,492 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Number;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(builder = FilterCondition.Builder.class)
-public class FilterCondition implements Filter {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Optional<List<String>> inMailboxes;
-        private Optional<List<String>> notInMailboxes;
-        private ZonedDateTime before;
-        private ZonedDateTime after;
-        private Number minSize;
-        private Number maxSize;
-        private Boolean isFlagged;
-        private Boolean isUnread;
-        private Boolean isAnswered;
-        private Boolean isForwarded;
-        private Boolean isDraft;
-        private Boolean hasAttachment;
-        private String text;
-        private String from;
-        private String to;
-        private String cc;
-        private String bcc;
-        private String subject;
-        private String body;
-        private String attachments;
-        private Header header;
-        private Optional<String> hasKeyword;
-        private Optional<String> notKeyword;
-        private Optional<String> attachmentFileName;
-
-        private Builder() {
-            inMailboxes = Optional.empty();
-            notInMailboxes = Optional.empty();
-            hasKeyword = Optional.empty();
-            notKeyword = Optional.empty();
-            attachmentFileName = Optional.empty();
-        }
-
-        public Builder inMailboxes(String... inMailboxes) {
-            this.inMailboxes = Optional.of(ImmutableList.copyOf(inMailboxes));
-            return this;
-        }
-
-        @JsonDeserialize
-        public Builder inMailboxes(Optional<List<String>> inMailboxes) {
-            this.inMailboxes = inMailboxes.map(ImmutableList::copyOf);
-            return this;
-        }
-
-        public Builder notInMailboxes(String... notInMailboxes) {
-            this.notInMailboxes = Optional.of(ImmutableList.copyOf(notInMailboxes));
-            return this;
-        }
-
-        @JsonDeserialize
-        public Builder notInMailboxes(Optional<List<String>> notInMailboxes) {
-            this.notInMailboxes = notInMailboxes.map(ImmutableList::copyOf);
-            return this;
-        }
-
-        @JsonDeserialize
-        public Builder hasKeyword(Optional<String> hasKeyword) {
-            this.hasKeyword = hasKeyword;
-            return this;
-        }
-
-        @JsonDeserialize
-        public Builder notKeyword(Optional<String> notKeyword) {
-            this.notKeyword = notKeyword;
-            return this;
-        }
-
-        public Builder before(ZonedDateTime before) {
-            this.before = before;
-            return this;
-        }
-
-        public Builder after(ZonedDateTime after) {
-            this.after = after;
-            return this;
-        }
-
-        public Builder minSize(long minSize) {
-            this.minSize = Number.DEFAULT_FACTORY.from(minSize)
-                .orElseThrow(() -> new IllegalArgumentException(Number.VALIDATION_MESSAGE));
-            return this;
-        }
-
-        public Builder maxSize(long maxSize) {
-            this.maxSize = Number.DEFAULT_FACTORY.from(maxSize)
-                .orElseThrow(() -> new IllegalArgumentException(Number.VALIDATION_MESSAGE));
-            return this;
-        }
-
-        public Builder isFlagged(boolean isFlagged) {
-            this.isFlagged = isFlagged;
-            return this;
-        }
-
-        public Builder isUnread(boolean isUnread) {
-            this.isUnread = isUnread;
-            return this;
-        }
-
-        public Builder isAnswered(boolean isAnswered) {
-            this.isAnswered = isAnswered;
-            return this;
-        }
-
-        public Builder isDraft(boolean isDraft) {
-            this.isDraft = isDraft;
-            return this;
-        }
-
-        public Builder isForwarded(boolean isForwarded) {
-            this.isForwarded = isForwarded;
-            return this;
-        }
-
-        public Builder hasAttachment(boolean hasAttachment) {
-            this.hasAttachment = hasAttachment;
-            return this;
-        }
-
-        public Builder text(String text) {
-            this.text = text;
-            return this;
-        }
-
-        public Builder from(String from) {
-            this.from = from;
-            return this;
-        }
-
-        public Builder to(String to) {
-            this.to = to;
-            return this;
-        }
-
-        public Builder cc(String cc) {
-            this.cc = cc;
-            return this;
-        }
-
-        public Builder bcc(String bcc) {
-            this.bcc = bcc;
-            return this;
-        }
-
-        public Builder subject(String subject) {
-            this.subject = subject;
-            return this;
-        }
-
-        public Builder body(String body) {
-            this.body = body;
-            return this;
-        }
-
-        public Builder attachments(String attachments) {
-            this.attachments = attachments;
-            return this;
-        }
-
-        public Builder header(Header header) {
-            this.header = header;
-            return this;
-        }
-
-        public Builder attachmentFileName(Optional<String> attachmentFileName) {
-            this.attachmentFileName = attachmentFileName;
-            return this;
-        }
-
-        public FilterCondition build() {
-            Preconditions.checkArgument(!hasKeyword.isPresent() || (Keyword.of(hasKeyword.get()) != null), "hasKeyword is not valid");
-            Preconditions.checkArgument(!notKeyword.isPresent() || (Keyword.of(notKeyword.get()) != null), "notKeyword is not valid");
-            return new FilterCondition(inMailboxes, notInMailboxes, Optional.ofNullable(before), Optional.ofNullable(after), Optional.ofNullable(minSize), Optional.ofNullable(maxSize),
-                    Optional.ofNullable(isFlagged), Optional.ofNullable(isUnread), Optional.ofNullable(isAnswered), Optional.ofNullable(isDraft), Optional.ofNullable(isForwarded),
-                    Optional.ofNullable(hasAttachment),
-                    Optional.ofNullable(text), Optional.ofNullable(from), Optional.ofNullable(to), Optional.ofNullable(cc), Optional.ofNullable(bcc), Optional.ofNullable(subject),
-                    Optional.ofNullable(body), Optional.ofNullable(attachments), Optional.ofNullable(header), hasKeyword, notKeyword, attachmentFileName);
-        }
-    }
-
-    private final Optional<List<String>> inMailboxes;
-    private final Optional<List<String>> notInMailboxes;
-    private final Optional<ZonedDateTime> before;
-    private final Optional<ZonedDateTime> after;
-    private final Optional<Number> minSize;
-    private final Optional<Number> maxSize;
-    private final Optional<Boolean> isFlagged;
-    private final Optional<Boolean> isUnread;
-    private final Optional<Boolean> isAnswered;
-    private final Optional<Boolean> isDraft;
-    private final Optional<Boolean> isForwarded;
-    private final Optional<Boolean> hasAttachment;
-    private final Optional<String> text;
-    private final Optional<String> from;
-    private final Optional<String> to;
-    private final Optional<String> cc;
-    private final Optional<String> bcc;
-    private final Optional<String> subject;
-    private final Optional<String> body;
-    private final Optional<String> attachments;
-    private final Optional<Header> header;
-    private final Optional<String> hasKeyword;
-    private final Optional<String> notKeyword;
-    private final Optional<String> attachmentFileName;
-
-    @VisibleForTesting FilterCondition(Optional<List<String>> inMailboxes, Optional<List<String>> notInMailboxes, Optional<ZonedDateTime> before, Optional<ZonedDateTime> after, Optional<Number> minSize, Optional<Number> maxSize,
-                                       Optional<Boolean> isFlagged, Optional<Boolean> isUnread, Optional<Boolean> isAnswered, Optional<Boolean> isDraft, Optional<Boolean> isForwarded,
-                                       Optional<Boolean> hasAttachment,
-                                       Optional<String> text, Optional<String> from, Optional<String> to, Optional<String> cc, Optional<String> bcc, Optional<String> subject,
-                                       Optional<String> body, Optional<String> attachments, Optional<Header> header, Optional<String> hasKeyword, Optional<String> notKeyword, Optional<String> attachmentFileName) {
-
-        this.inMailboxes = inMailboxes;
-        this.notInMailboxes = notInMailboxes;
-        this.before = before;
-        this.after = after;
-        this.minSize = minSize;
-        this.maxSize = maxSize;
-        this.isFlagged = isFlagged;
-        this.isUnread = isUnread;
-        this.isAnswered = isAnswered;
-        this.isDraft = isDraft;
-        this.isForwarded = isForwarded;
-        this.hasAttachment = hasAttachment;
-        this.text = text;
-        this.from = from;
-        this.to = to;
-        this.cc = cc;
-        this.bcc = bcc;
-        this.subject = subject;
-        this.body = body;
-        this.attachments = attachments;
-        this.header = header;
-        this.hasKeyword = hasKeyword;
-        this.notKeyword = notKeyword;
-        this.attachmentFileName = attachmentFileName;
-    }
-
-    @Override
-    public boolean inMailboxFilterOnly() {
-        return inMailboxes.filter(list -> list.size() == 1).isPresent()
-            && after.isEmpty()
-            && noOtherFiltersSet();
-    }
-
-    @Override
-    public boolean inMailboxAndAfterFiltersOnly() {
-        return inMailboxes.filter(list -> list.size() == 1).isPresent()
-            && after.isPresent()
-            && noOtherFiltersSet();
-    }
-
-    private boolean noOtherFiltersSet() {
-        return notInMailboxes.isEmpty()
-            && before.isEmpty()
-            && minSize.isEmpty()
-            && maxSize.isEmpty()
-            && isFlagged.isEmpty()
-            && isUnread.isEmpty()
-            && isAnswered.isEmpty()
-            && isDraft.isEmpty()
-            && isForwarded.isEmpty()
-            && hasAttachment.isEmpty()
-            && text.isEmpty()
-            && from.isEmpty()
-            && to.isEmpty()
-            && cc.isEmpty()
-            && bcc.isEmpty()
-            && subject.isEmpty()
-            && body.isEmpty()
-            && attachments.isEmpty()
-            && header.isEmpty()
-            && hasKeyword.isEmpty()
-            && notKeyword.isEmpty()
-            && attachmentFileName.isEmpty();
-    }
-
-    public Optional<List<String>> getInMailboxes() {
-        return inMailboxes;
-    }
-
-    public Optional<List<String>> getNotInMailboxes() {
-        return notInMailboxes;
-    }
-
-    public Optional<ZonedDateTime> getBefore() {
-        return before;
-    }
-
-    public Optional<ZonedDateTime> getAfter() {
-        return after;
-    }
-
-    public Optional<Number> getMinSize() {
-        return minSize;
-    }
-
-    public Optional<Number> getMaxSize() {
-        return maxSize;
-    }
-
-    public Optional<Boolean> getIsFlagged() {
-        return isFlagged;
-    }
-
-    public Optional<Boolean> getIsUnread() {
-        return isUnread;
-    }
-
-    public Optional<Boolean> getIsAnswered() {
-        return isAnswered;
-    }
-
-    public Optional<Boolean> getIsDraft() {
-        return isDraft;
-    }
-
-    public Optional<Boolean> getIsForwarded() {
-        return isForwarded;
-    }
-
-    public Optional<Boolean> getHasAttachment() {
-        return hasAttachment;
-    }
-
-    public Optional<String> getText() {
-        return text;
-    }
-
-    public Optional<String> getFrom() {
-        return from;
-    }
-
-    public Optional<String> getTo() {
-        return to;
-    }
-
-    public Optional<String> getCc() {
-        return cc;
-    }
-
-    public Optional<String> getBcc() {
-        return bcc;
-    }
-
-    public Optional<String> getSubject() {
-        return subject;
-    }
-
-    public Optional<String> getBody() {
-        return body;
-    }
-
-    public Optional<String> getAttachments() {
-        return attachments;
-    }
-
-    public Optional<Header> getHeader() {
-        return header;
-    }
-
-    public Optional<String> getHasKeyword() {
-        return hasKeyword;
-    }
-
-    public Optional<String> getNotKeyword() {
-        return notKeyword;
-    }
-
-    public Optional<String> getAttachmentFileName() {
-        return attachmentFileName;
-    }
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof FilterCondition) {
-            FilterCondition other = (FilterCondition) obj;
-            return Objects.equals(this.inMailboxes, other.inMailboxes)
-                && Objects.equals(this.notInMailboxes, other.notInMailboxes)
-                && Objects.equals(this.before, other.before)
-                && Objects.equals(this.after, other.after)
-                && Objects.equals(this.minSize, other.minSize)
-                && Objects.equals(this.maxSize, other.maxSize)
-                && Objects.equals(this.isFlagged, other.isFlagged)
-                && Objects.equals(this.isUnread, other.isUnread)
-                && Objects.equals(this.isAnswered, other.isAnswered)
-                && Objects.equals(this.isDraft, other.isDraft)
-                && Objects.equals(this.isForwarded, other.isForwarded)
-                && Objects.equals(this.hasAttachment, other.hasAttachment)
-                && Objects.equals(this.text, other.text)
-                && Objects.equals(this.from, other.from)
-                && Objects.equals(this.to, other.to)
-                && Objects.equals(this.cc, other.cc)
-                && Objects.equals(this.bcc, other.bcc)
-                && Objects.equals(this.subject, other.subject)
-                && Objects.equals(this.body, other.body)
-                && Objects.equals(this.attachments, other.attachments)
-                && Objects.equals(this.header, other.header)
-                && Objects.equals(this.hasKeyword, other.hasKeyword)
-                && Objects.equals(this.notKeyword, other.notKeyword)
-                && Objects.equals(this.attachmentFileName, other.attachmentFileName);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(inMailboxes, notInMailboxes, before, after, minSize, maxSize, isFlagged, isUnread, isAnswered, isDraft, isForwarded, hasAttachment,
-                text, from, to, cc, bcc, subject, body, attachments, header, hasKeyword, notKeyword, attachmentFileName);
-    }
-
-    @Override
-    public String toString() {
-        ToStringHelper helper = MoreObjects.toStringHelper(getClass());
-        inMailboxes.ifPresent(x -> helper.add("inMailboxes", x));
-        notInMailboxes.ifPresent(x -> helper.add("notInMailboxes", x));
-        before.ifPresent(x -> helper.add("before", x));
-        after.ifPresent(x -> helper.add("after", x));
-        minSize.ifPresent(x -> helper.add("minSize", x));
-        maxSize.ifPresent(x -> helper.add("maxSize", x));
-        isFlagged.ifPresent(x -> helper.add("isFlagged", x));
-        isUnread.ifPresent(x -> helper.add("isUnread", x));
-        isAnswered.ifPresent(x -> helper.add("isAnswered", x));
-        isDraft.ifPresent(x -> helper.add("isDraft", x));
-        isForwarded.ifPresent(x -> helper.add("isForwarded", x));
-        hasAttachment.ifPresent(x -> helper.add("hasAttachment", x));
-        text.ifPresent(x -> helper.add("text", x));
-        from.ifPresent(x -> helper.add("from", x));
-        to.ifPresent(x -> helper.add("to", x));
-        cc.ifPresent(x -> helper.add("cc", x));
-        bcc.ifPresent(x -> helper.add("bcc", x));
-        subject.ifPresent(x -> helper.add("subject", x));
-        body.ifPresent(x -> helper.add("body", x));
-        attachments.ifPresent(x -> helper.add("attachments", x));
-        header.ifPresent(x -> helper.add("header", x));
-        hasKeyword.ifPresent(x -> helper.add("hasKeyword", x));
-        notKeyword.ifPresent(x -> helper.add("notKeyword", x));
-        attachmentFileName.ifPresent(x -> helper.add("attachmentFileName", x));
-        return helper.toString();
-    }
-
-    @Override
-    public String prettyPrint(String indentation) {
-        return indentation + toString() + "\n";
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterOperator.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterOperator.java
deleted file mode 100644
index c0c0b21..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterOperator.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(builder = FilterOperator.Builder.class)
-public class FilterOperator implements Filter {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Operator operator;
-        private final ImmutableList.Builder<Filter> conditionsBuilder;
-
-        private Builder() {
-            conditionsBuilder = ImmutableList.builder();
-        }
-
-        public Builder operator(Operator operator) {
-            Preconditions.checkNotNull(operator);
-            this.operator = operator;
-            return this;
-        }
-
-        public Builder conditions(List<Filter> conditions) {
-            this.conditionsBuilder.addAll(conditions);
-            return this;
-        }
-
-        public FilterOperator build() {
-            Preconditions.checkState(operator != null, "'operator' is mandatory");
-            ImmutableList<Filter> conditions = conditionsBuilder.build();
-            Preconditions.checkState(!conditions.isEmpty(), "'conditions' is mandatory");
-            return new FilterOperator(operator, conditions);
-        }
-    }
-
-    public static FilterOperator or(Filter... filters) {
-        Preconditions.checkArgument(filters.length > 0);
-        return builder().operator(Operator.OR).conditions(ImmutableList.copyOf(filters)).build();
-    }
-
-    public static FilterOperator and(Filter... filters) {
-        Preconditions.checkArgument(filters.length > 0);
-        return builder().operator(Operator.AND).conditions(ImmutableList.copyOf(filters)).build();
-    }
-
-
-    public static FilterOperator not(Filter... filters) {
-        Preconditions.checkArgument(filters.length > 0);
-        return builder().operator(Operator.NOT).conditions(ImmutableList.copyOf(filters)).build();
-    }
-    
-    private final Operator operator;
-    private final List<Filter> conditions;
-
-    @VisibleForTesting FilterOperator(Operator operator, List<Filter> conditions) {
-        this.operator = operator;
-        this.conditions = conditions;
-    }
-
-    public Operator getOperator() {
-        return operator;
-    }
-
-    public List<Filter> getConditions() {
-        return conditions;
-    }
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof FilterOperator) {
-            FilterOperator other = (FilterOperator) obj;
-            return Objects.equals(this.operator, other.operator)
-                && Objects.equals(this.conditions, other.conditions);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(operator, conditions);
-    }
-
-    @Override
-    public String toString() {
-        return prettyPrint("");
-    }
-
-    @Override
-    public String prettyPrint(String indentation) {
-        return indentation
-            + MoreObjects.toStringHelper(getClass())
-                    .add("operator", operator)
-                    .toString()
-            + "\n"
-            + conditionListToString(indentation + "  ");
-    }
-
-    private String conditionListToString(String indentation) {
-        return conditions.stream()
-            .map(condition -> condition.prettyPrint(indentation))
-            .collect(Collectors.joining());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterRequest.java
deleted file mode 100644
index 5782b38..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterRequest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.methods.JmapRequest;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-
-@JsonDeserialize(builder = GetFilterRequest.Builder.class)
-public class GetFilterRequest implements JmapRequest {
-    private static final String ISSUER = "GetFilterRequest";
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Builder() {
-
-        }
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-            }
-            return this;
-        }
-
-        public GetFilterRequest build() {
-            return new GetFilterRequest();
-        }
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private GetFilterRequest() {
-
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterResponse.java
deleted file mode 100644
index 8399529..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetFilterResponse.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-
-import org.apache.james.jmap.api.filtering.Rule;
-import org.apache.james.jmap.methods.Method;
-
-import com.google.common.collect.ImmutableList;
-
-public class GetFilterResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private String accountId;
-        private String state;
-        private ImmutableList.Builder<JmapRuleDTO> rules;
-
-        public Builder() {
-            this.rules = ImmutableList.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            this.accountId = accountId;
-            return this;
-        }
-
-        public Builder state(String state) {
-            this.state = state;
-            return this;
-        }
-
-        public Builder rules(List<Rule> rules) {
-            this.rules.addAll(rules.stream()
-                .map(JmapRuleDTO::from)
-                .collect(ImmutableList.toImmutableList()));
-            return this;
-        }
-
-        public GetFilterResponse build() {
-            return new GetFilterResponse(accountId, state, rules.build());
-        }
-    }
-
-    private final String accountId;
-    private final String state;
-    private final List<JmapRuleDTO> rules;
-
-    private GetFilterResponse(String accountId, String state, List<JmapRuleDTO> rules) {
-        this.accountId = accountId;
-        this.state = state;
-        this.rules = rules;
-    }
-
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public String getState() {
-        return state;
-    }
-
-    public List<JmapRuleDTO> getSingleton() {
-        return rules;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesRequest.java
deleted file mode 100644
index 9dfb375..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesRequest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-@JsonDeserialize(builder = GetMailboxesRequest.Builder.class)
-public class GetMailboxesRequest implements JmapRequest {
-    private static final String ISSUER = "GetMailboxesRequest";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private String accountId;
-        private Optional<ImmutableSet<MailboxProperty>> properties;
-        private Optional<ImmutableList<MailboxId>> ids;
-
-        private Builder() {
-            ids = Optional.empty();
-            properties = Optional.empty();
-        }
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-            }
-            return this;
-        }
-
-        public Builder ids(List<MailboxId> ids) {
-            if (ids != null) {
-                this.ids = Optional.of(ImmutableList.copyOf(ids));
-            }
-            return this;
-        }
-
-        public Builder properties(List<String> properties) {
-            this.properties = Optional.of(
-                properties.stream()
-                    .map(MailboxProperty::findProperty)
-                    .flatMap(Optional::stream)
-                    .collect(ImmutableSet.toImmutableSet()));
-            return this;
-        }
-        
-        public GetMailboxesRequest build() {
-            return new GetMailboxesRequest(Optional.ofNullable(accountId), ids, properties);
-        }
-    }
-
-    private final Optional<String> accountId;
-    private final Optional<ImmutableList<MailboxId>> ids;
-    private final Optional<ImmutableSet<MailboxProperty>> properties;
-
-    private GetMailboxesRequest(Optional<String> accountId, Optional<ImmutableList<MailboxId>> ids, Optional<ImmutableSet<MailboxProperty>> properties) {
-        this.accountId = accountId;
-        this.ids = ids;
-        this.properties = properties;
-    }
-
-    public Optional<String> getAccountId() {
-        return accountId;
-    }
-
-    public Optional<ImmutableList<MailboxId>> getIds() {
-        return ids;
-    }
-
-    public Optional<ImmutableSet<MailboxProperty>> getProperties() {
-        return properties;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesResponse.java
deleted file mode 100644
index 0e8cb6f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMailboxesResponse.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.methods.Method;
-
-import com.google.common.collect.ImmutableList;
-
-public class GetMailboxesResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private String accountId;
-        private String state;
-        private final ImmutableList.Builder<Mailbox> mailboxes;
-        private final ImmutableList.Builder<String> notFoundBuilder;
-
-        private Builder() {
-            mailboxes = ImmutableList.builder();
-            notFoundBuilder = ImmutableList.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new NotImplementedException("not implemented");
-            }
-            return this;
-        }
-
-        public Builder state(String state) {
-            if (state != null) {
-                throw new NotImplementedException("not implemented");
-            }
-            return this;
-        }
-
-        public Builder add(Mailbox mailbox) {
-            this.mailboxes.add(mailbox);
-            return this;
-        }
-
-        public Builder addAll(List<Mailbox> list) {
-            this.mailboxes.addAll(list);
-            return this;
-        }
-        
-        public Builder notFound(String[] notFound) {
-            if (notFound != null) {
-                throw new NotImplementedException("not implemented");
-            }
-            return this;
-        }
-
-        public GetMailboxesResponse build() {
-            ImmutableList<String> notFound = notFoundBuilder.build();
-            return new GetMailboxesResponse(accountId, state, mailboxes.build(), 
-                    notFound.isEmpty() ? Optional.empty() : Optional.of(notFound));
-        }
-    }
-
-    private final String accountId;
-    private final String state;
-    private final List<Mailbox> list;
-    private final Optional<List<String>> notFound;
-
-    private GetMailboxesResponse(String accountId, String state, List<Mailbox> list, Optional<List<String>> notFound) {
-        this.accountId = accountId;
-        this.state = state;
-        this.list = list;
-        this.notFound = notFound;
-    }
-
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public String getState() {
-        return state;
-    }
-
-    public List<Mailbox> getList() {
-        return list;
-    }
-
-    public Optional<List<String>> getNotFound() {
-        return notFound;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListRequest.java
deleted file mode 100644
index 249850e..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListRequest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.model.Number;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(builder = GetMessageListRequest.Builder.class)
-public class GetMessageListRequest implements JmapRequest {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private String accountId;
-        private Filter filter;
-        private final ImmutableList.Builder<String> sort;
-        private Boolean collapseThreads;
-        private Optional<Number> position;
-        private String anchor;
-        private Number anchorOffset;
-        private Number limit;
-        private Boolean fetchThreads;
-        private Boolean fetchMessages;
-        private final ImmutableList.Builder<String> fetchMessageProperties;
-        private Boolean fetchSearchSnippets;
-
-        private Builder() {
-            position = Optional.empty();
-            sort = ImmutableList.builder();
-            fetchMessageProperties = ImmutableList.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder filter(Filter filter) {
-            this.filter = filter;
-            return this;
-        }
-
-        public Builder sort(List<String> sort) {
-            this.sort.addAll(sort);
-            return this;
-        }
-
-        public Builder collapseThreads(boolean collapseThreads) {
-            this.collapseThreads = collapseThreads;
-            return this;
-        }
-
-        public Builder position(long position) {
-            this.position = Optional.of(
-                Number.DEFAULT_FACTORY.from(position)
-                    .orElseThrow(() -> new IllegalArgumentException(Number.VALIDATION_MESSAGE)));
-            return this;
-        }
-
-        public Builder anchor(String anchor) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder anchorOffset(int anchorOffset) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder limit(long limit) {
-            this.limit = Number.DEFAULT_FACTORY.from(limit)
-                .orElseThrow(() -> new IllegalArgumentException(Number.VALIDATION_MESSAGE));
-            return this;
-        }
-
-        public Builder fetchThreads(boolean fetchThreads) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder fetchMessages(boolean fetchMessages) {
-            this.fetchMessages = fetchMessages;
-            return this;
-        }
-
-        public Builder fetchMessageProperties(List<String> fetchMessageProperties) {
-            this.fetchMessageProperties.addAll(fetchMessageProperties);
-            return this;
-        }
-
-        public Builder fetchSearchSnippets(boolean fetchSearchSnippets) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public GetMessageListRequest build() {
-            return new GetMessageListRequest(Optional.ofNullable(accountId), Optional.ofNullable(filter), sort.build(), Optional.ofNullable(collapseThreads),
-                    position, Optional.ofNullable(anchor), Optional.ofNullable(anchorOffset), Optional.ofNullable(limit), Optional.ofNullable(fetchThreads),
-                    Optional.ofNullable(fetchMessages), fetchMessageProperties.build(), Optional.ofNullable(fetchSearchSnippets));
-        }
-    }
-
-    private final Optional<String> accountId;
-    private final Optional<Filter> filter;
-    private final List<String> sort;
-    private final Optional<Boolean> collapseThreads;
-    private final Optional<Number> position;
-    private final Optional<String> anchor;
-    private final Optional<Number> anchorOffset;
-    private final Optional<Number> limit;
-    private final Optional<Boolean> fetchThreads;
-    private final Optional<Boolean> fetchMessages;
-    private final List<String> fetchMessageProperties;
-    private final Optional<Boolean> fetchSearchSnippets;
-
-    @VisibleForTesting GetMessageListRequest(Optional<String> accountId, Optional<Filter> filter, List<String> sort, Optional<Boolean> collapseThreads,
-            Optional<Number> position, Optional<String> anchor, Optional<Number> anchorOffset, Optional<Number> limit, Optional<Boolean> fetchThreads,
-            Optional<Boolean> fetchMessages, List<String> fetchMessageProperties, Optional<Boolean> fetchSearchSnippets) {
-
-        this.accountId = accountId;
-        this.filter = filter;
-        this.sort = sort;
-        this.collapseThreads = collapseThreads;
-        this.position = position;
-        this.anchor = anchor;
-        this.anchorOffset = anchorOffset;
-        this.limit = limit;
-        this.fetchThreads = fetchThreads;
-        this.fetchMessages = fetchMessages;
-        this.fetchMessageProperties = fetchMessageProperties;
-        this.fetchSearchSnippets = fetchSearchSnippets;
-    }
-
-    public Optional<String> getAccountId() {
-        return accountId;
-    }
-
-    public Optional<Filter> getFilter() {
-        return filter;
-    }
-
-    public List<String> getSort() {
-        return sort;
-    }
-
-    public Optional<Boolean> isCollapseThreads() {
-        return collapseThreads;
-    }
-
-    public Optional<Number> getPosition() {
-        return position;
-    }
-
-    public Optional<String> getAnchor() {
-        return anchor;
-    }
-
-    public Optional<Number> getAnchorOffset() {
-        return anchorOffset;
-    }
-
-    public Optional<Number> getLimit() {
-        return limit;
-    }
-
-    public Optional<Boolean> isFetchThreads() {
-        return fetchThreads;
-    }
-
-    public Optional<Boolean> isFetchMessages() {
-        return fetchMessages;
-    }
-
-    public List<String> getFetchMessageProperties() {
-        return fetchMessageProperties;
-    }
-
-    public Optional<Boolean> isFetchSearchSnippets() {
-        return fetchSearchSnippets;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListResponse.java
deleted file mode 100644
index 7eadc9b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessageListResponse.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.mailbox.model.MessageId;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-
-public class GetMessageListResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private String accountId;
-        private Filter filter;
-        private final ImmutableList.Builder<String> sort;
-        private boolean collapseThreads;
-        private String state;
-        private boolean canCalculateUpdates;
-        private Optional<Number> position;
-        private Optional<Number> total;
-        private final ImmutableList.Builder<String> threadIds;
-        private final ImmutableList.Builder<MessageId> messageIds;
-
-        private Builder() {
-            sort = ImmutableList.builder();
-            threadIds = ImmutableList.builder();
-            messageIds = ImmutableList.builder();
-            position = Optional.empty();
-            total = Optional.empty();
-        }
-
-        public Builder accountId(String accountId) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder filter(Filter filter) {
-            this.filter = filter;
-            return this;
-        }
-
-        public Builder sort(List<String> sort) {
-            this.sort.addAll(sort);
-            return this;
-        }
-
-        public Builder collapseThreads(boolean collapseThreads) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder state(String state) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder canCalculateUpdates(boolean canCalculateUpdates) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder position(int position) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder total(int total) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder threadIds(List<String> threadIds) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder messageId(MessageId messageId) {
-            this.messageIds.add(messageId);
-            return this;
-        }
-
-        public Builder messageIds(List<MessageId> messageIds) {
-            this.messageIds.addAll(messageIds);
-            return this;
-        }
-
-        public GetMessageListResponse build() {
-            return new GetMessageListResponse(accountId, filter, sort.build(), collapseThreads, state,
-                    canCalculateUpdates, position.orElse(Number.ZERO), total.orElse(Number.ZERO), threadIds.build(), messageIds.build());
-        }
-    }
-
-    private final String accountId;
-    private final Filter filter;
-    private final List<String> sort;
-    private final boolean collapseThreads;
-    private final String state;
-    private final boolean canCalculateUpdates;
-    private final Number position;
-    private final Number total;
-    private final List<String> threadIds;
-    private final List<MessageId> messageIds;
-
-    @VisibleForTesting GetMessageListResponse(String accountId, Filter filter, List<String> sort, boolean collapseThreads, String state,
-            boolean canCalculateUpdates, Number position, Number total, List<String> threadIds, List<MessageId> messageIds) {
-
-        this.accountId = accountId;
-        this.filter = filter;
-        this.sort = sort;
-        this.collapseThreads = collapseThreads;
-        this.state = state;
-        this.canCalculateUpdates = canCalculateUpdates;
-        this.position = position;
-        this.total = total;
-        this.threadIds = threadIds;
-        this.messageIds = messageIds;
-    }
-
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public Filter getFilter() {
-        return filter;
-    }
-
-    public List<String> getSort() {
-        return sort;
-    }
-
-    public boolean isCollapseThreads() {
-        return collapseThreads;
-    }
-
-    public String getState() {
-        return state;
-    }
-
-    public boolean isCanCalculateUpdates() {
-        return canCalculateUpdates;
-    }
-
-    public Number getPosition() {
-        return position;
-    }
-
-    public Number getTotal() {
-        return total;
-    }
-
-    public List<String> getThreadIds() {
-        return threadIds;
-    }
-
-    public List<MessageId> getMessageIds() {
-        return messageIds;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesRequest.java
deleted file mode 100644
index b1a8b2d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesRequest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.mailbox.model.MessageId;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-@JsonDeserialize(builder = GetMessagesRequest.Builder.class)
-public class GetMessagesRequest implements JmapRequest {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-    
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        
-        private Optional<String> accountId;
-        private final ImmutableList.Builder<MessageId> ids;
-        private Optional<ImmutableSet<String>> properties;
-
-        private Builder() {
-            accountId = Optional.empty();
-            ids = ImmutableList.builder();
-            properties = Optional.empty();
-        }
-        
-        public Builder accountId(String accountId) {
-            this.accountId = Optional.of(accountId);
-            return this;
-        }
-
-        public Builder ids(List<MessageId> ids) {
-            this.ids.addAll(ids);
-            return this;
-        }
-
-        public Builder properties(List<String> properties) {
-            this.properties = Optional.ofNullable(properties).map(ImmutableSet::copyOf);
-            return this;
-        }
-        
-        public GetMessagesRequest build() {
-            return new GetMessagesRequest(accountId, ids.build(), new MessageProperties(properties));
-        }
-    }
-    
-    private final Optional<String> accountId;
-    private final ImmutableList<MessageId> ids;
-    private final MessageProperties properties;
-
-    public GetMessagesRequest(Optional<String> accountId, ImmutableList<MessageId> ids, MessageProperties properties) {
-        this.accountId = accountId;
-        this.ids = ids;
-        this.properties = properties;
-    }
-    
-    public Optional<String> getAccountId() {
-        return accountId;
-    }
-    
-    public ImmutableList<MessageId> getIds() {
-        return ids;
-    }
-    
-    public MessageProperties getProperties() {
-        return properties;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesResponse.java
deleted file mode 100644
index 0c19824..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetMessagesResponse.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.message.view.MessageView;
-import org.apache.james.mailbox.model.MessageId;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-
-@JsonDeserialize(builder = GetMessagesResponse.Builder.class)
-public class GetMessagesResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-    
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private ImmutableSet<MessageView> messages;
-        private Set<MessageId> expectedMessageIds;
-
-        private Builder() {
-            this.messages = ImmutableSet.of();
-        }
-
-        @JsonIgnore
-        public Builder message(MessageView message) {
-            this.messages = ImmutableSet.of(message);
-            return this;
-        }
-
-        public Builder messages(Collection<? extends MessageView> messages) {
-            this.messages = ImmutableSet.copyOf(messages);
-            return this;
-        }
-
-        public Builder expectedMessageIds(List<MessageId> expectedMessageIds) {
-            this.expectedMessageIds = ImmutableSet.copyOf(expectedMessageIds);
-            return this;
-        }
-        
-        public GetMessagesResponse build() {
-            Preconditions.checkState(messages != null);
-            return new GetMessagesResponse(messages, messagesNotFound());
-        }
-        
-
-        private Set<MessageId> messagesNotFound() {
-            if (expectedMessageIds.size() == messages.size()) {
-                return ImmutableSet.of();
-            }
-            return Sets.difference(expectedMessageIds,
-                messages.stream().map(MessageView::getId).collect(Collectors.toSet()));
-        }
-    }
-    
-    
-    
-    private final Set<MessageView> messages;
-    private final Set<MessageId> messagesNotFound;
-
-    private GetMessagesResponse(Set<MessageView> messages, Set<MessageId> messagesNotFound) {
-        this.messages = messages;
-        this.messagesNotFound = messagesNotFound;
-    }
-
-    @JsonSerialize
-    public Set<MessageView> list() {
-        return messages;
-    }
-    
-    @JsonSerialize
-    public Set<MessageId> notFound() {
-        return messagesNotFound;
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationRequest.java
deleted file mode 100644
index b161ebd..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationRequest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.methods.JmapRequest;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-
-@JsonDeserialize(builder = GetVacationRequest.Builder.class)
-public class GetVacationRequest implements JmapRequest {
-    private static final String ISSUER = "GetVacationRequest";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        public Builder accountId(String accountId) {
-            throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-        }
-
-        public GetVacationRequest build() {
-            return new GetVacationRequest();
-        }
-    }
-
-    private GetVacationRequest() {
-
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationResponse.java
deleted file mode 100644
index b817f88..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/GetVacationResponse.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.james.jmap.methods.Method;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class GetVacationResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private String accountId;
-        private VacationResponse vacationResponse;
-
-        public Builder accountId(String accountId) {
-            this.accountId = accountId;
-            return this;
-        }
-
-        public Builder vacationResponse(VacationResponse vacationResponse) {
-            this.vacationResponse = vacationResponse;
-            return this;
-        }
-
-        public GetVacationResponse build() {
-            Preconditions.checkArgument(vacationResponse != null, "Account should contain exactly one vacation response");
-            return new GetVacationResponse(accountId, vacationResponse);
-        }
-    }
-
-    private final String accountId;
-    private final List<VacationResponse> list;
-
-    private GetVacationResponse(String accountId, VacationResponse vacationResponse) {
-        this.accountId = accountId;
-        this.list = ImmutableList.of(vacationResponse);
-    }
-
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public List<VacationResponse> getList() {
-        return list;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        GetVacationResponse that = (GetVacationResponse) o;
-
-        return Objects.equals(this.accountId, that.accountId)
-            && Objects.equals(this.list, that.list);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(accountId, list);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Header.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Header.java
deleted file mode 100644
index 7fccc36..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Header.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Optional;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-
-public class Header {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonCreator
-    public static Header from(List<String> header) {
-        return builder().header(header).build();
-    }
-
-    public static class Builder {
-        
-        private String name;
-        private Optional<String> value;
-
-        public Builder header(List<String> header) {
-            Preconditions.checkNotNull(header);
-            Preconditions.checkArgument(header.size() > 0, "'header' should contains at least one element");
-            Preconditions.checkArgument(header.size() < 3, "'header' should contains lesser than three elements");
-            this.name = header.get(0);
-            this.value = Optional.ofNullable(Iterables.get(header, 1, null));
-            return this;
-        }
-
-        public Header build() {
-            Preconditions.checkState(!Strings.isNullOrEmpty(name), "'name' is mandatory");
-            return new Header(name, value);
-        }
-    }
-
-    private final String name;
-    private final Optional<String> value;
-
-    private Header(String name, Optional<String> value) {
-        this.name = name;
-        this.value = value;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Optional<String> getValue() {
-        return value;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/InvocationRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/InvocationRequest.java
deleted file mode 100644
index a0dc395..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/InvocationRequest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.base.Preconditions;
-
-public class InvocationRequest {
-
-    public static InvocationRequest deserialize(JsonNode[] json) {
-        Preconditions.checkState(json.length == 3, "should have three elements");
-        Preconditions.checkState(json[0].isTextual(), "first element should be a String");
-        Preconditions.checkState(json[1].isObject(), "second element should be a Json");
-        Preconditions.checkState(json[2].isTextual(), "third element should be a String");
-        return new InvocationRequest(Method.Request.name(json[0].textValue()), (ObjectNode) json[1], MethodCallId.of(json[2].textValue()));
-    }
-
-    private final Method.Request.Name method;
-    private final ObjectNode parameters;
-    private final MethodCallId methodCallId;
-
-    protected InvocationRequest(Method.Request.Name method, ObjectNode parameters, MethodCallId methodCallId) {
-        this.method = method;
-        this.parameters = parameters;
-        this.methodCallId = methodCallId;
-    }
-
-    public Method.Request.Name getMethodName() {
-        return method;
-    }
-
-    public ObjectNode getParameters() {
-        return parameters;
-    }
-
-    public MethodCallId getMethodCallId() {
-        return methodCallId;
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapMDN.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapMDN.java
deleted file mode 100644
index 6532a55..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapMDN.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.apache.james.core.Domain;
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.InvalidOriginMessageForMDNException;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mdn.MDN;
-import org.apache.james.mdn.MDNReport;
-import org.apache.james.mdn.fields.Disposition;
-import org.apache.james.mdn.fields.ReportingUserAgent;
-import org.apache.james.mime4j.codec.DecodeMonitor;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.address.AddressList;
-import org.apache.james.mime4j.dom.address.Mailbox;
-import org.apache.james.mime4j.dom.address.MailboxList;
-import org.apache.james.mime4j.dom.field.AddressListField;
-import org.apache.james.mime4j.dom.field.ParseException;
-import org.apache.james.mime4j.field.AddressListFieldLenientImpl;
-import org.apache.james.mime4j.util.MimeUtil;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(builder = JmapMDN.Builder.class)
-public class JmapMDN {
-
-    public static final String DISPOSITION_NOTIFICATION_TO = "Disposition-Notification-To";
-    public static final String RETURN_PATH = "Return-Path";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private MessageId messageId;
-        private String subject;
-        private String textBody;
-        private String reportingUA;
-        private MDNDisposition disposition;
-
-        public Builder messageId(MessageId messageId) {
-            this.messageId = messageId;
-            return this;
-        }
-
-        public Builder subject(String subject) {
-            this.subject = subject;
-            return this;
-        }
-
-        public Builder textBody(String textBody) {
-            this.textBody = textBody;
-            return this;
-        }
-
-        public Builder reportingUA(String reportingUA) {
-            this.reportingUA = reportingUA;
-            return this;
-        }
-
-        public Builder disposition(MDNDisposition disposition) {
-            this.disposition = disposition;
-            return this;
-        }
-
-        public JmapMDN build() {
-            Preconditions.checkState(messageId != null, "'messageId' is mandatory");
-            Preconditions.checkState(subject != null, "'subject' is mandatory");
-            Preconditions.checkState(textBody != null, "'textBody' is mandatory");
-            Preconditions.checkState(reportingUA != null, "'reportingUA' is mandatory");
-            Preconditions.checkState(disposition != null, "'disposition' is mandatory");
-
-            return new JmapMDN(messageId, subject, textBody, reportingUA, disposition);
-        }
-
-    }
-
-    private final MessageId messageId;
-    private final String subject;
-    private final String textBody;
-    private final String reportingUA;
-    private final MDNDisposition disposition;
-
-    @VisibleForTesting
-    JmapMDN(MessageId messageId, String subject, String textBody, String reportingUA, MDNDisposition disposition) {
-        this.messageId = messageId;
-        this.subject = subject;
-        this.textBody = textBody;
-        this.reportingUA = reportingUA;
-        this.disposition = disposition;
-    }
-
-    public MessageId getMessageId() {
-        return messageId;
-    }
-
-    public String getSubject() {
-        return subject;
-    }
-
-    public String getTextBody() {
-        return textBody;
-    }
-
-    public ReportingUserAgent getReportingUA() {
-        return ReportingUserAgent.builder().userAgentName(reportingUA).build();
-    }
-
-    public MDNDisposition getDisposition() {
-        return disposition;
-    }
-
-    public Message generateMDNMessage(Message originalMessage, MailboxSession mailboxSession) throws ParseException, IOException, InvalidOriginMessageForMDNException {
-
-        Username username = mailboxSession.getUser();
-
-        return MDN.builder()
-            .report(generateReport(originalMessage, mailboxSession))
-            .humanReadableText(textBody)
-            .build()
-        .asMime4JMessageBuilder()
-            .setTo(getSenderAddress(originalMessage))
-            .setFrom(username.asString())
-            .setSubject(subject)
-            .setDate(new Date())
-            .setMessageId(MimeUtil.createUniqueMessageId(username.getDomainPart().map(Domain::name).orElse(null)))
-            .build();
-    }
-
-    private String getSenderAddress(Message originalMessage) throws InvalidOriginMessageForMDNException {
-        return getAddressForHeader(originalMessage, DISPOSITION_NOTIFICATION_TO)
-            .orElseThrow(() -> InvalidOriginMessageForMDNException.missingHeader(DISPOSITION_NOTIFICATION_TO))
-            .getAddress();
-    }
-
-    private Optional<Mailbox> getAddressForHeader(Message originalMessage, String fieldName) {
-        return Optional.ofNullable(originalMessage.getHeader()
-            .getFields(fieldName))
-            .orElse(ImmutableList.of())
-            .stream()
-            .map(field -> AddressListFieldLenientImpl.PARSER.parse(field, new DecodeMonitor()))
-            .findFirst()
-            .map(AddressListField::getAddressList)
-            .map(AddressList::flatten)
-            .map(MailboxList::stream)
-            .orElse(Stream.of())
-            .findFirst();
-    }
-
-    private MDNReport generateReport(Message originalMessage, MailboxSession mailboxSession) throws InvalidOriginMessageForMDNException {
-        if (originalMessage.getMessageId() == null) {
-            throw InvalidOriginMessageForMDNException.missingHeader("Message-ID");
-        }
-        return MDNReport.builder()
-            .dispositionField(generateDisposition())
-            .originalRecipientField(mailboxSession.getUser().asString())
-            .originalMessageIdField(originalMessage.getMessageId())
-            .finalRecipientField(mailboxSession.getUser().asString())
-            .reportingUserAgentField(getReportingUA())
-            .build();
-    }
-
-    private Disposition generateDisposition() {
-        return Disposition.builder()
-            .actionMode(disposition.getActionMode())
-            .sendingMode(disposition.getSendingMode())
-            .type(disposition.getType())
-            .build();
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof JmapMDN) {
-            JmapMDN that = (JmapMDN) o;
-
-            return Objects.equals(this.messageId, that.messageId)
-                && Objects.equals(this.subject, that.subject)
-                && Objects.equals(this.textBody, that.textBody)
-                && Objects.equals(this.reportingUA, that.reportingUA)
-                && Objects.equals(this.disposition, that.disposition);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(messageId, subject, textBody, reportingUA, disposition);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("messageId", messageId)
-            .add("subject", subject)
-            .add("textBody", textBody)
-            .add("reportingUA", reportingUA)
-            .add("mdnDisposition", disposition)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapRuleDTO.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapRuleDTO.java
deleted file mode 100644
index 1823766..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/JmapRuleDTO.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-
-import org.apache.james.jmap.api.filtering.Rule;
-import org.apache.james.jmap.draft.model.deserialization.JmapRuleDTODeserializer;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(using = JmapRuleDTODeserializer.class)
-public class JmapRuleDTO {
-
-    public static class ConditionDTO {
-
-        public static ConditionDTO from(Rule.Condition condition) {
-            return new ConditionDTO(
-                condition.getField().asString(),
-                condition.getComparator().asString(),
-                condition.getValue());
-        }
-
-        private final String field;
-        private final String comparator;
-        private final String value;
-
-        @JsonCreator
-        public ConditionDTO(@JsonProperty("field") String field,
-                            @JsonProperty("comparator") String comparator,
-                            @JsonProperty("value") String value) {
-            this.field = field;
-            this.comparator = comparator;
-            this.value = value;
-        }
-
-        public String getField() {
-            return field;
-        }
-
-        public String getComparator() {
-            return comparator;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        public Rule.Condition toCondition() {
-            return Rule.Condition.of(
-                    Rule.Condition.Field.of(field),
-                    Rule.Condition.Comparator.of(comparator),
-                    value);
-        }
-    }
-
-    public static class ActionDTO {
-
-        public static class AppendInMailboxesDTO {
-
-            public static AppendInMailboxesDTO from(Rule.Action.AppendInMailboxes appendInMailboxes) {
-                return new AppendInMailboxesDTO(appendInMailboxes.getMailboxIds());
-            }
-
-            @JsonCreator
-            public AppendInMailboxesDTO(@JsonProperty("mailboxIds") List<String> mailboxIds) {
-                this.mailboxIds = ImmutableList.copyOf(mailboxIds);
-            }
-
-            private final List<String> mailboxIds;
-
-            public List<String> getMailboxIds() {
-                return mailboxIds;
-            }
-
-            public Rule.Action.AppendInMailboxes toAppendInMailboxes() {
-                return Rule.Action.AppendInMailboxes.withMailboxIds(mailboxIds);
-            }
-        }
-
-        public static ActionDTO from(Rule.Action action) {
-            return new ActionDTO(AppendInMailboxesDTO.from(action.getAppendInMailboxes()));
-        }
-
-        @JsonCreator
-        public ActionDTO(@JsonProperty("appendIn") AppendInMailboxesDTO appendIn) {
-            this.appendIn = appendIn;
-        }
-
-        private final AppendInMailboxesDTO appendIn;
-
-        public AppendInMailboxesDTO getAppendIn() {
-            return appendIn;
-        }
-
-        public Rule.Action toAction() {
-            return Rule.Action.of(appendIn.toAppendInMailboxes());
-        }
-    }
-
-    public static ImmutableList<Rule> toRules(List<JmapRuleDTO> ruleDTOList) {
-        Preconditions.checkNotNull(ruleDTOList);
-        return ruleDTOList.stream()
-                .map(JmapRuleDTO::toRule)
-                .collect(ImmutableList.toImmutableList());
-    }
-
-    public static ImmutableList<JmapRuleDTO> from(List<Rule> rules) {
-        Preconditions.checkNotNull(rules);
-        return rules.stream()
-            .map(JmapRuleDTO::from)
-            .collect(ImmutableList.toImmutableList());
-    }
-
-    public static JmapRuleDTO from(Rule rule) {
-        return new JmapRuleDTO(rule.getId().asString(),
-            rule.getName(),
-            ConditionDTO.from(rule.getConditionGroup().getConditions().get(0)),
-            ActionDTO.from(rule.getAction()));
-    }
-
-    private final String id;
-    private final String name;
-    private final ConditionDTO conditionDTO;
-    private final ActionDTO actionDTO;
-
-    @JsonCreator
-    public JmapRuleDTO(@JsonProperty("id") String id,
-                       @JsonProperty("name") String name,
-                       @JsonProperty("condition") ConditionDTO conditionDTO,
-                       @JsonProperty("action") ActionDTO actionDTO) {
-        this.name = name;
-        this.conditionDTO = conditionDTO;
-        this.actionDTO = actionDTO;
-        this.id = id;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public ConditionDTO getCondition() {
-        return conditionDTO;
-    }
-
-    public ActionDTO getAction() {
-        return actionDTO;
-    }
-
-    public Rule toRule() {
-        return Rule.builder()
-            .id(Rule.Id.of(id))
-            .name(name)
-            .conditionGroup(Rule.ConditionGroup.of(Rule.ConditionCombiner.AND, ImmutableList.of(conditionDTO.toCondition())))
-            .action(actionDTO.toAction())
-            .build();
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("id", id)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MDNDisposition.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MDNDisposition.java
deleted file mode 100644
index cf6ea2b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MDNDisposition.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Objects;
-
-import org.apache.james.mdn.action.mode.DispositionActionMode;
-import org.apache.james.mdn.sending.mode.DispositionSendingMode;
-import org.apache.james.mdn.type.DispositionType;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-@JsonDeserialize(builder = MDNDisposition.Builder.class)
-public class MDNDisposition {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private DispositionActionMode actionMode;
-        private DispositionSendingMode sendingMode;
-        private DispositionType type;
-
-        public Builder actionMode(DispositionActionMode actionMode) {
-            this.actionMode = actionMode;
-            return this;
-        }
-
-        public Builder sendingMode(DispositionSendingMode sendingMode) {
-            this.sendingMode = sendingMode;
-            return this;
-        }
-
-        public Builder type(DispositionType type) {
-            this.type = type;
-            return this;
-        }
-
-        public MDNDisposition build() {
-            Preconditions.checkState(actionMode != null, "'actionMode' is mandatory");
-            Preconditions.checkState(sendingMode != null, "'sendingMode' is mandatory");
-            Preconditions.checkState(type != null, "'type' is mandatory");
-
-            return new MDNDisposition(actionMode, sendingMode, type);
-        }
-    }
-
-    private final DispositionActionMode actionMode;
-    private final DispositionSendingMode sendingMode;
-    private final DispositionType type;
-
-    @VisibleForTesting
-    MDNDisposition(DispositionActionMode actionMode, DispositionSendingMode sendingMode, DispositionType type) {
-        this.actionMode = actionMode;
-        this.sendingMode = sendingMode;
-        this.type = type;
-    }
-
-    public DispositionActionMode getActionMode() {
-        return actionMode;
-    }
-
-    public DispositionSendingMode getSendingMode() {
-        return sendingMode;
-    }
-
-    public DispositionType getType() {
-        return type;
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof MDNDisposition) {
-            MDNDisposition that = (MDNDisposition) o;
-
-            return Objects.equals(this.actionMode, that.actionMode)
-                && Objects.equals(this.sendingMode, that.sendingMode)
-                && Objects.equals(this.type, that.type);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(actionMode, sendingMode, type);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("actionMode", actionMode)
-            .add("sendingMode", sendingMode)
-            .add("type", type)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxCreationId.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxCreationId.java
deleted file mode 100644
index d696e46..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxCreationId.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Objects;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.base.MoreObjects;
-
-public class MailboxCreationId {
-
-    public static MailboxCreationId of(String creationId) {
-        return new MailboxCreationId(creationId);
-    }
-
-    private final String creationId;
-
-    private MailboxCreationId(String creationId) {
-        this.creationId = creationId;
-    }
-
-    @JsonValue
-    public String getCreationId() {
-        return creationId;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof MailboxCreationId) {
-            return Objects.equals(creationId, ((MailboxCreationId) obj).creationId);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(creationId);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("creationId", creationId)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxFactory.java
deleted file mode 100644
index 4cb4126..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxFactory.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxNamespace;
-import org.apache.james.jmap.draft.model.mailbox.SortOrder;
-import org.apache.james.jmap.draft.utils.quotas.DefaultQuotaLoader;
-import org.apache.james.jmap.draft.utils.quotas.QuotaLoader;
-import org.apache.james.jmap.model.mailbox.Rights;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.model.MailboxACL;
-import org.apache.james.mailbox.model.MailboxCounters;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxMetaData;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.primitives.Booleans;
-
-import reactor.core.publisher.Mono;
-
-public class MailboxFactory {
-    private static class MailboxTuple {
-        public static MailboxTuple from(MailboxMetaData metaData) {
-            return new MailboxTuple(metaData.getPath(), metaData.getCounters().sanitize(), metaData.getResolvedAcls());
-        }
-
-        public static Mono<MailboxTuple> from(MessageManager messageManager, MailboxSession session) {
-            return Mono.from(messageManager.getMailboxCountersReactive(session))
-                .map(MailboxCounters::sanitize)
-                .map(Throwing.function(counters -> new MailboxTuple(messageManager.getMailboxPath(), counters, messageManager.getResolvedAcl(session))));
-        }
-
-        private final MailboxPath mailboxPath;
-        private final MailboxCounters.Sanitized mailboxCounters;
-        private final MailboxACL acl;
-
-        private MailboxTuple(MailboxPath mailboxPath, MailboxCounters.Sanitized mailboxCounters, MailboxACL acl) {
-            this.mailboxPath = mailboxPath;
-            this.mailboxCounters = mailboxCounters;
-            this.acl = acl;
-        }
-    }
-
-    private final MailboxManager mailboxManager;
-    private final QuotaManager quotaManager;
-    private final QuotaRootResolver quotaRootResolver;
-
-    public static class MailboxBuilder {
-        private final MailboxFactory mailboxFactory;
-        private QuotaLoader quotaLoader;
-        private MailboxSession session;
-        private Optional<MailboxId> id = Optional.empty();
-        private Optional<MailboxMetaData> mailboxMetaData = Optional.empty();
-        private Optional<Map<MailboxPath, MailboxMetaData>> userMailboxesMetadata = Optional.empty();
-
-        private MailboxBuilder(MailboxFactory mailboxFactory, QuotaLoader quotaLoader) {
-            this.mailboxFactory = mailboxFactory;
-            this.quotaLoader = quotaLoader;
-        }
-
-        public MailboxBuilder id(MailboxId id) {
-            this.id = Optional.of(id);
-            return this;
-        }
-
-        public MailboxBuilder mailboxMetadata(MailboxMetaData mailboxMetaData) {
-            this.mailboxMetaData = Optional.of(mailboxMetaData);
-            return this;
-        }
-
-        public MailboxBuilder session(MailboxSession session) {
-            this.session = session;
-            return this;
-        }
-
-        public MailboxBuilder quotaLoader(QuotaLoader quotaLoader) {
-            this.quotaLoader = quotaLoader;
-            return this;
-        }
-
-        public MailboxBuilder usingPreloadedMailboxesMetadata(Optional<Map<MailboxPath, MailboxMetaData>> userMailboxesMetadata) {
-            this.userMailboxesMetadata = userMailboxesMetadata;
-            return this;
-        }
-
-        public Mono<Mailbox> build() {
-            Preconditions.checkNotNull(session);
-
-            MailboxId mailboxId = computeMailboxId();
-
-            Mono<MailboxTuple> mailbox = mailboxMetaData.map(MailboxTuple::from)
-                .map(Mono::just)
-                .orElse(Mono.from(mailboxFactory.mailboxManager.getMailboxReactive(mailboxId, session))
-                    .flatMap(messageManager -> MailboxTuple.from(messageManager, session)));
-
-            return mailbox.flatMap(tuple -> mailboxFactory.from(
-                mailboxId,
-                tuple.mailboxPath,
-                tuple.mailboxCounters,
-                tuple.acl,
-                userMailboxesMetadata,
-                quotaLoader,
-                session))
-                .onErrorResume(MailboxNotFoundException.class, e -> Mono.empty());
-        }
-
-        private MailboxId computeMailboxId() {
-            int idCount = Booleans.countTrue(id.isPresent(), mailboxMetaData.isPresent());
-            Preconditions.checkState(idCount == 1, "You need exactly one 'id' 'mailboxMetaData'");
-            return id.or(
-                () -> mailboxMetaData.map(MailboxMetaData::getId))
-                .get();
-        }
-
-        private Mono<MessageManager> mailbox(MailboxId mailboxId) {
-            return Mono.from(mailboxFactory.mailboxManager.getMailboxReactive(mailboxId, session));
-        }
-
-        private Mono<MessageManager> retrieveCachedMailbox(MailboxId mailboxId, Mono<MessageManager> mailbox) throws MailboxNotFoundException {
-            return mailbox
-                .onErrorResume(MailboxNotFoundException.class, any -> Mono.empty())
-                .switchIfEmpty(Mono.error(() -> new MailboxNotFoundException(mailboxId)));
-        }
-    }
-
-    @Inject
-    public MailboxFactory(MailboxManager mailboxManager, QuotaManager quotaManager, QuotaRootResolver quotaRootResolver) {
-        this.mailboxManager = mailboxManager;
-        this.quotaManager = quotaManager;
-        this.quotaRootResolver = quotaRootResolver;
-    }
-
-    public MailboxBuilder builder() {
-        QuotaLoader defaultQuotaLoader = new DefaultQuotaLoader(quotaRootResolver, quotaManager);
-        return new MailboxBuilder(this, defaultQuotaLoader);
-    }
-
-    private Mono<Mailbox> from(MailboxId mailboxId,
-                         MailboxPath mailboxPath,
-                         MailboxCounters.Sanitized mailboxCounters,
-                         MailboxACL resolvedAcl,
-                         Optional<Map<MailboxPath, MailboxMetaData>> userMailboxesMetadata,
-                         QuotaLoader quotaLoader,
-                         MailboxSession mailboxSession) {
-        boolean isOwner = mailboxPath.belongsTo(mailboxSession);
-        Optional<Role> role = Role.from(mailboxPath.getName())
-            .filter(any -> mailboxPath.belongsTo(mailboxSession));
-
-        Rights rights = Rights.fromACL(resolvedAcl)
-            .removeEntriesFor(mailboxPath.getUser());
-        Username username = mailboxSession.getUser();
-
-        return Mono.zip(
-                quotaLoader.getQuotas(mailboxPath),
-                getParentIdFromMailboxPath(mailboxPath, userMailboxesMetadata, mailboxSession))
-            .map(tuple -> Mailbox.builder()
-                .id(mailboxId)
-                .name(getName(mailboxPath, mailboxSession))
-                .parentId(tuple.getT2().orElse(null))
-                .role(role)
-                .unreadMessages(mailboxCounters.getUnseen())
-                .totalMessages(mailboxCounters.getCount())
-                .sortOrder(SortOrder.getSortOrder(role))
-                .sharedWith(rights)
-                .mayAddItems(rights.mayAddItems(username).orElse(isOwner))
-                .mayCreateChild(rights.mayCreateChild(username).orElse(isOwner))
-                .mayDelete(rights.mayDelete(username).orElse(isOwner))
-                .mayReadItems(rights.mayReadItems(username).orElse(isOwner))
-                .mayRemoveItems(rights.mayRemoveItems(username).orElse(isOwner))
-                .mayRename(rights.mayRename(username).orElse(isOwner))
-                .namespace(getNamespace(mailboxPath, isOwner))
-                .quotas(tuple.getT1())
-                .build());
-    }
-
-    private MailboxNamespace getNamespace(MailboxPath mailboxPath, boolean isOwner) {
-        if (isOwner) {
-            return MailboxNamespace.personal();
-        }
-        return MailboxNamespace.delegated(mailboxPath.getUser());
-    }
-
-    @VisibleForTesting
-    String getName(MailboxPath mailboxPath, MailboxSession mailboxSession) {
-        String name = mailboxPath.getName();
-        if (name.contains(String.valueOf(mailboxSession.getPathDelimiter()))) {
-            List<String> levels = Splitter.on(mailboxSession.getPathDelimiter()).splitToList(name);
-            return levels.get(levels.size() - 1);
-        }
-        return name;
-    }
-
-    @VisibleForTesting
-    Mono<Optional<MailboxId>> getParentIdFromMailboxPath(MailboxPath mailboxPath, Optional<Map<MailboxPath, MailboxMetaData>> userMailboxesMetadata,
-                                                   MailboxSession mailboxSession) {
-        List<MailboxPath> levels = mailboxPath.getHierarchyLevels(mailboxSession.getPathDelimiter());
-        if (levels.size() <= 1) {
-            return Mono.just(Optional.empty());
-        }
-        MailboxPath parent = levels.get(levels.size() - 2);
-        return userMailboxesMetadata.map(list -> Mono.just(retrieveParentFromMetadata(parent, list)))
-            .orElseGet(Throwing.supplier(() -> retrieveParentFromBackend(mailboxSession, parent)).sneakyThrow());
-    }
-
-    private Mono<Optional<MailboxId>> retrieveParentFromBackend(MailboxSession mailboxSession, MailboxPath parent) {
-        return Mono.from(mailboxManager.getMailboxReactive(parent, mailboxSession))
-            .map(MessageManager::getId)
-            .map(Optional::of);
-    }
-
-    private Optional<MailboxId> retrieveParentFromMetadata(MailboxPath parent, Map<MailboxPath, MailboxMetaData> userMailboxesMetadata) {
-        return Optional.ofNullable(userMailboxesMetadata.get(parent)).map(MailboxMetaData::getId);
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxProperty.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxProperty.java
deleted file mode 100644
index 457e757..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/MailboxProperty.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.Arrays;
-import java.util.Optional;
-
-import org.apache.james.jmap.model.Property;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-public enum MailboxProperty implements Property {
-    ID("id"),
-    NAME("name"),
-    PARENT_ID("parentId"),
-    ROLE("role"),
-    SORT_ORDER("sortOrder"),
-    MUST_BE_ONLY_MAILBOX("mustBeOnlyMailbox"),
-    MAY_READ_ITEMS("mayReadItems"),
-    MAY_ADD_ITEMS("mayAddItems"),
-    MAY_REMOVE_ITEMS("mayRemoveItems"),
-    MAY_CREATE_CHILD("mayCreateChild"),
-    MAY_RENAME("mayRename"),
-    MAY_DELETE("mayDelete"),
-    TOTAL_MESSAGES("totalMessages"),
-    UNREAD_MESSAGES("unreadMessages"),
-    TOTAL_THREADS("totalThreads"),
-    UNREAD_THREADS("unreadThreads");
-
-    private final String fieldName;
-
-    MailboxProperty(String fieldName) {
-        this.fieldName = fieldName;
-    }
-
-    @Override
-    public String asFieldName() {
-        return fieldName;
-    }
-
-    public static Optional<MailboxProperty> findProperty(String value) {
-        Preconditions.checkNotNull(value);
-        return Arrays.stream(values())
-            .filter(element -> element.fieldName.equals(value))
-            .findAny();
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("fieldName", fieldName)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/OldKeyword.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/OldKeyword.java
deleted file mode 100644
index 3344608..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/OldKeyword.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import jakarta.mail.Flags;
-
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableSet;
-
-public class OldKeyword {
-
-    public static class Builder {
-        private Optional<Boolean> isUnread;
-        private Optional<Boolean> isFlagged;
-        private Optional<Boolean> isAnswered;
-        private Optional<Boolean> isDraft;
-        private Optional<Boolean> isForwarded;
-
-        private Builder() {
-            isUnread = Optional.empty();
-            isFlagged = Optional.empty();
-            isAnswered = Optional.empty();
-            isDraft = Optional.empty();
-            isForwarded = Optional.empty();
-        }
-
-        public Builder isFlagged(Optional<Boolean> isFlagged) {
-            this.isFlagged = isFlagged;
-            return this;
-        }
-
-        public Builder isFlagged(boolean isFlagged) {
-            return isFlagged(Optional.of(isFlagged));
-        }
-
-        public Builder isUnread(Optional<Boolean> isUnread) {
-            this.isUnread = isUnread;
-            return this;
-        }
-
-        public Builder isUnread(boolean isUnread) {
-            return isUnread(Optional.of(isUnread));
-        }
-
-        public Builder isAnswered(Optional<Boolean> isAnswered) {
-            this.isAnswered = isAnswered;
-            return this;
-        }
-
-        public Builder isAnswered(boolean isAnswered) {
-            return isAnswered(Optional.of(isAnswered));
-        }
-
-        public Builder isDraft(Optional<Boolean> isDraft) {
-            this.isDraft = isDraft;
-            return this;
-        }
-
-        public Builder isDraft(boolean isDraft) {
-            return isDraft(Optional.of(isDraft));
-        }
-
-        public Builder isForwarded(Optional<Boolean> isForwarded) {
-            this.isForwarded = isForwarded;
-            return this;
-        }
-
-        public Builder isForwarded(boolean isForwarded) {
-            return isForwarded(Optional.of(isForwarded));
-        }
-
-        public Optional<OldKeyword> computeOldKeyword() {
-            if (isAnswered.isPresent() || isFlagged.isPresent() || isUnread.isPresent() || isForwarded.isPresent() || isDraft.isPresent()) {
-                return Optional.of(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-            }
-
-            return Optional.empty();
-        }
-
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private final Optional<Boolean> isUnread;
-    private final Optional<Boolean> isFlagged;
-    private final Optional<Boolean> isAnswered;
-    private final Optional<Boolean> isDraft;
-    private final Optional<Boolean> isForwarded;
-
-    private OldKeyword(Optional<Boolean> isUnread, Optional<Boolean> isFlagged, Optional<Boolean> isAnswered,
-                      Optional<Boolean> isDraft, Optional<Boolean> isForwarded) {
-        this.isUnread = isUnread;
-        this.isFlagged = isFlagged;
-        this.isAnswered = isAnswered;
-        this.isDraft = isDraft;
-        this.isForwarded = isForwarded;
-    }
-
-    public Optional<Boolean> isUnread() {
-        return isUnread;
-    }
-
-    public Optional<Boolean> isFlagged() {
-        return isFlagged;
-    }
-
-    public Optional<Boolean> isAnswered() {
-        return isAnswered;
-    }
-
-    public Optional<Boolean> isDraft() {
-        return isDraft;
-    }
-
-    public Optional<Boolean> isForwarded() {
-        return isForwarded;
-    }
-
-    public Flags applyToState(Flags currentFlags) {
-        Flags newStateFlags = new Flags();
-
-        if (isFlagged().orElse(currentFlags.contains(Flags.Flag.FLAGGED))) {
-            newStateFlags.add(Flags.Flag.FLAGGED);
-        }
-        if (isAnswered().orElse(currentFlags.contains(Flags.Flag.ANSWERED))) {
-            newStateFlags.add(Flags.Flag.ANSWERED);
-        }
-        if (isDraft().orElse(currentFlags.contains(Flags.Flag.DRAFT))) {
-            newStateFlags.add(Flags.Flag.DRAFT);
-        }
-        if (isForwarded().orElse(currentFlags.contains(new Flags("$Forwarded")))) {
-            newStateFlags.add(new Flags("$Forwarded"));
-        }
-        boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(currentFlags.contains(Flags.Flag.SEEN));
-        if (shouldMessageBeMarkSeen) {
-            newStateFlags.add(Flags.Flag.SEEN);
-        }
-        Arrays.stream(currentFlags.getUserFlags())
-            .forEach(newStateFlags::add);
-        if (currentFlags.contains(Flags.Flag.RECENT)) {
-            newStateFlags.add(Flags.Flag.RECENT);
-        }
-        if (currentFlags.contains(Flags.Flag.DELETED)) {
-            newStateFlags.add(Flags.Flag.DELETED);
-        }
-        return newStateFlags;
-    }
-
-    public Keywords asKeywords() {
-        return Keywords.strictFactory()
-            .fromSet(
-                Stream.of(
-                    isAnswered.filter(b -> b).map(b -> Keyword.ANSWERED),
-                    isDraft.filter(b -> b).map(b -> Keyword.DRAFT),
-                    isForwarded.filter(b -> b).map(b -> Keyword.FORWARDED),
-                    isFlagged.filter(b -> b).map(b -> Keyword.FLAGGED),
-                    isUnread.filter(b -> !b).map(b -> Keyword.SEEN))
-                .flatMap(Optional::stream)
-                .collect(ImmutableSet.toImmutableSet()));
-    }
-
-    @Override
-    public final boolean equals(Object other) {
-        if (other instanceof OldKeyword) {
-            OldKeyword oldKeyword = (OldKeyword) other;
-            return Objects.equal(isUnread, oldKeyword.isUnread)
-                && Objects.equal(isFlagged, oldKeyword.isFlagged)
-                && Objects.equal(isAnswered, oldKeyword.isAnswered)
-                && Objects.equal(isDraft, oldKeyword.isDraft)
-                && Objects.equal(isForwarded, oldKeyword.isForwarded);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hashCode(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("isUnread", isUnread)
-                .add("isFlagged", isFlagged)
-                .add("isAnswered", isAnswered)
-                .add("isDraft", isDraft)
-                .add("isForwarded", isForwarded)
-                .toString();
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Operator.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Operator.java
deleted file mode 100644
index 5a542d2..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Operator.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public enum Operator {
-
-    @JsonProperty("AND")
-    AND,
-    @JsonProperty("OR")
-    OR,
-    @JsonProperty("NOT")
-    NOT
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetError.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetError.java
deleted file mode 100644
index c8f7ae0..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetError.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Optional;
-import java.util.Set;
-
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-
-@JsonDeserialize(builder = SetError.Builder.class)
-public class SetError {
-
-    public enum Type {
-        INVALID_ARGUMENTS("invalidArguments"),
-        INVALID_PROPERTIES("invalidProperties"),
-        ERROR("anErrorOccurred"),
-        MAX_QUOTA_REACHED("maxQuotaReached"),
-        MAILBOX_HAS_CHILD("mailboxHasChild"),
-        NOT_FOUND("notFound");
-
-        private final String stringValue;
-
-        Type(String stringValue) {
-            this.stringValue = stringValue;
-        }
-
-        public String asString() {
-            return stringValue;
-        }
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Type type;
-        private String description;
-        private Optional<ImmutableSet<MessageProperty>> properties = Optional.empty();
-
-        protected Builder() {
-        }
-
-        public Builder type(Type type) {
-            this.type = type;
-            return this;
-        }
-
-        public Builder description(String description) {
-            this.description = description;
-            return this;
-        }
-
-        public Builder properties(MessageProperty... properties) {
-            return properties(ImmutableSet.copyOf(properties));
-        }
-
-        public Builder properties(Set<MessageProperty> properties) {
-            this.properties = Optional.of(Sets.union(
-                    this.properties.orElse(ImmutableSet.of()),
-                    Optional.ofNullable(properties).orElse(ImmutableSet.of()))
-                    .immutableCopy());
-            return this;
-        }
-
-        public SetError build() {
-            Preconditions.checkState(type != null, "'type' is mandatory");
-            return new SetError(type, Optional.ofNullable(description), properties);
-        }
-    }
-
-    private final Type type;
-    private final Optional<String> description;
-    private final Optional<ImmutableSet<MessageProperty>> properties;
-
-
-    @VisibleForTesting SetError(Type type, Optional<String> description, Optional<ImmutableSet<MessageProperty>> properties) {
-        this.type = type;
-        this.description = description;
-        this.properties = properties;
-    }
-
-    protected SetError(SetError setError) {
-        this.type = setError.type;
-        this.description = setError.description;
-        this.properties = setError.properties;
-    }
-
-    
-    @JsonSerialize
-    public String getType() {
-        return type.asString();
-    }
-
-    @JsonSerialize
-    public Optional<String> getDescription() {
-        return description;
-    }
-
-    @JsonSerialize
-    public Optional<ImmutableSet<MessageProperty>> getProperties() {
-        return properties;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof SetError) {
-            SetError other = (SetError) obj;
-            return Objects.equal(this.type, other.type)
-                && Objects.equal(this.description, other.description)
-                && Objects.equal(this.properties, other.properties);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(type, description, properties);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("description", description)
-                .add("type", type)
-                .add("properties", properties)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterRequest.java
deleted file mode 100644
index e25ffc7..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterRequest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.methods.JmapRequest;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.collect.ImmutableList;
-
-@JsonDeserialize(builder = SetFilterRequest.Builder.class)
-public class SetFilterRequest implements JmapRequest {
-
-    private static final String ISSUER = "SetFilterRequest";
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private final ImmutableList.Builder<JmapRuleDTO> rules;
-
-        private Builder() {
-            this.rules = ImmutableList.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-            }
-            return this;
-        }
-
-        public Builder ifInState(String ifInState) {
-            if (ifInState != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "ifInState");
-            }
-            return this;
-        }
-
-        public Builder singleton(List<JmapRuleDTO> rules) {
-            this.rules.addAll(rules);
-            return this;
-        }
-
-        public SetFilterRequest build() {
-            return new SetFilterRequest(rules.build());
-        }
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private final List<JmapRuleDTO> singleton;
-
-    private SetFilterRequest(List<JmapRuleDTO> singleton) {
-        this.singleton = singleton;
-    }
-
-    public List<JmapRuleDTO> getSingleton() {
-        return singleton;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterResponse.java
deleted file mode 100644
index 3708053..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetFilterResponse.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.james.jmap.methods.Method;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetFilterResponse implements Method.Response {
-    public static final String SINGLETON = "singleton";
-
-    public static SetFilterResponse updated() {
-        return new SetFilterResponse(ImmutableList.of(SINGLETON), ImmutableMap.of());
-    }
-
-    public static SetFilterResponse notUpdated(SetError error) {
-        return new SetFilterResponse(ImmutableList.of(), ImmutableMap.of(SINGLETON, error));
-    }
-
-    private final List<String> updated;
-    private final Map<String, SetError> notUpdated;
-
-    private SetFilterResponse(List<String> updated, Map<String, SetError> notUpdated) {
-        this.updated = updated;
-        this.notUpdated = notUpdated;
-    }
-
-    public List<String> getUpdated() {
-        return updated;
-    }
-
-    public Map<String, SetError> getNotUpdated() {
-        return notUpdated;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesRequest.java
deleted file mode 100644
index 807ad6b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesRequest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.draft.model.mailbox.MailboxCreateRequest;
-import org.apache.james.jmap.draft.model.mailbox.MailboxUpdateRequest;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-@JsonDeserialize(builder = SetMailboxesRequest.Builder.class)
-public class SetMailboxesRequest implements JmapRequest {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private final ImmutableMap.Builder<MailboxCreationId, MailboxCreateRequest> create;
-        private final ImmutableMap.Builder<MailboxId, MailboxUpdateRequest> update;
-        private final ImmutableList.Builder<MailboxId> destroy;
-
-        private Builder() {
-            create = ImmutableMap.builder();
-            update = ImmutableMap.builder();
-            destroy = ImmutableList.builder();
-        }
-
-        public Builder create(Map<MailboxCreationId, MailboxCreateRequest> requests) {
-            create.putAll(requests);
-            return this;
-        }
-
-        public Builder create(MailboxCreationId creationId, MailboxCreateRequest mailbox) {
-            create.put(creationId, mailbox);
-            return this;
-        }
-        
-        public Builder accountId(String accountId) {
-            throw new NotImplementedException("not implemented");
-        }
-        
-        public Builder ifInState(String state) {
-            throw new NotImplementedException("not implemented");
-        }
-        
-        public Builder update(MailboxId mailboxId, MailboxUpdateRequest mailboxUpdateRequest) {
-            update.put(mailboxId, mailboxUpdateRequest);
-            return this;
-        }
-        
-        public Builder update(Map<MailboxId, MailboxUpdateRequest> updates) {
-            update.putAll(updates);
-            return this;
-        }
-        
-        public Builder destroy(List<MailboxId> deletions) {
-            destroy.addAll(deletions);
-            return this;
-        }
-
-        public SetMailboxesRequest build() {
-            return new SetMailboxesRequest(create.build(), update.build(), destroy.build());
-        }
-    }
-
-    private final ImmutableMap<MailboxCreationId, MailboxCreateRequest> create;
-    private final ImmutableMap<MailboxId, MailboxUpdateRequest> update;
-    private final ImmutableList<MailboxId> destroy;
-
-    @VisibleForTesting
-    SetMailboxesRequest(ImmutableMap<MailboxCreationId, MailboxCreateRequest> create, ImmutableMap<MailboxId,MailboxUpdateRequest> update, ImmutableList<MailboxId> destroy) {
-        this.create = create;
-        this.update = update;
-        this.destroy = destroy;
-    }
-
-    public ImmutableMap<MailboxCreationId, MailboxCreateRequest> getCreate() {
-        return create;
-    }
-
-    public ImmutableMap<MailboxId,MailboxUpdateRequest> getUpdate() {
-        return update;
-    }
-
-    public ImmutableList<MailboxId> getDestroy() {
-        return destroy;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesResponse.java
deleted file mode 100644
index bc9d684..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMailboxesResponse.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMailboxesResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private final ImmutableMap.Builder<MailboxCreationId, Mailbox> created;
-        private final ImmutableList.Builder<MailboxId> updated;
-        private final ImmutableList.Builder<MailboxId> destroyed;
-        private final ImmutableMap.Builder<MailboxCreationId, SetError> notCreated;
-        private final ImmutableMap.Builder<MailboxId, SetError> notUpdated;
-        private final ImmutableMap.Builder<MailboxId, SetError> notDestroyed;
-
-        private Builder() {
-            created = ImmutableMap.builder();
-            updated = ImmutableList.builder();
-            destroyed = ImmutableList.builder();
-            notCreated = ImmutableMap.builder();
-            notUpdated = ImmutableMap.builder();
-            notDestroyed = ImmutableMap.builder();
-        }
-
-        public Builder created(MailboxCreationId creationId, Mailbox mailbox) {
-            created.put(creationId, mailbox);
-            return this;
-        }
-
-        public Builder created(ImmutableMap<MailboxCreationId, Mailbox> created) {
-            this.created.putAll(created);
-            return this;
-        }
-
-        public Builder updated(MailboxId mailboxId) {
-            updated.add(mailboxId);
-            return this;
-        }
-        
-        public Builder updated(List<MailboxId> mailboxIds) {
-            updated.addAll(mailboxIds);
-            return this;
-        }
-        
-        public Builder destroyed(MailboxId mailboxId) {
-            destroyed.add(mailboxId);
-            return this;
-        }
-        
-        public Builder destroyed(ImmutableList<MailboxId> destroyed) {
-            this.destroyed.addAll(destroyed);
-            return this;
-        }
-        
-        public Builder notCreated(Map<MailboxCreationId, SetError> notCreated) {
-            this.notCreated.putAll(notCreated);
-            return this;
-        }
-
-        public Builder notCreated(MailboxCreationId mailboxCreationId, SetError setError) {
-            this.notCreated.put(mailboxCreationId, setError);
-            return this;
-        }
-        
-        public Builder notUpdated(MailboxId mailboxId, SetError setError) {
-            notUpdated.put(mailboxId, setError);
-            return this;
-        }
-        
-        public Builder notUpdated(Map<MailboxId, SetError> notUpdated) {
-            this.notUpdated.putAll(notUpdated);
-            return this;
-        }
-
-        public Builder notDestroyed(MailboxId mailboxId, SetError setError) {
-            notDestroyed.put(mailboxId, setError);
-            return this;
-        }
-
-        public Builder notDestroyed(ImmutableMap<MailboxId, SetError> notDestroyed) {
-            this.notDestroyed.putAll(notDestroyed);
-            return this;
-        }
-
-        public SetMailboxesResponse build() {
-            return new SetMailboxesResponse(created.build(), updated.build(), destroyed.build(), notCreated.build(), notUpdated.build(), notDestroyed.build());
-        }
-    }
-
-    private final ImmutableMap<MailboxCreationId, Mailbox> created;
-    private final ImmutableList<MailboxId> updated;
-    private final ImmutableList<MailboxId> destroyed;
-    private final ImmutableMap<MailboxCreationId, SetError> notCreated;
-    private final ImmutableMap<MailboxId,SetError> notUpdated;
-    private final ImmutableMap<MailboxId,SetError> notDestroyed;
-
-    @VisibleForTesting
-    SetMailboxesResponse(ImmutableMap<MailboxCreationId, Mailbox> created, ImmutableList<MailboxId> updated, ImmutableList<MailboxId> destroyed,
-            ImmutableMap<MailboxCreationId, SetError> notCreated, ImmutableMap<MailboxId, SetError> notUpdated, ImmutableMap<MailboxId, SetError> notDestroyed) {
-        this.created = created;
-        this.updated = updated;
-        this.destroyed = destroyed;
-        this.notCreated = notCreated;
-        this.notUpdated = notUpdated;
-        this.notDestroyed = notDestroyed;
-    }
-
-    public ImmutableMap<MailboxCreationId, Mailbox> getCreated() {
-        return created;
-    }
-
-    public ImmutableList<MailboxId> getUpdated() {
-        return updated;
-    }
-
-    public ImmutableList<MailboxId> getDestroyed() {
-        return destroyed;
-    }
-    
-    public Map<MailboxCreationId, SetError> getNotCreated() {
-        return notCreated;
-    }
-
-    public ImmutableMap<MailboxId,SetError> getNotUpdated() {
-        return notUpdated;
-    }
-    
-    public ImmutableMap<MailboxId,SetError> getNotDestroyed() {
-        return notDestroyed;
-    }
-
-    public SetMailboxesResponse.Builder mergeInto(SetMailboxesResponse.Builder responseBuilder) {
-        return responseBuilder
-            .created(getCreated())
-            .updated(getUpdated())
-            .destroyed(getDestroyed())
-            .notCreated(getNotCreated())
-            .notUpdated(getNotUpdated())
-            .notDestroyed(getNotDestroyed());
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(created, notCreated, destroyed, notDestroyed);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof SetMailboxesResponse) {
-            SetMailboxesResponse other = (SetMailboxesResponse) obj;
-            return Objects.equal(this.created, other.created)
-                && Objects.equal(this.updated, other.updated)
-                && Objects.equal(this.destroyed, other.destroyed)
-                && Objects.equal(this.notCreated, other.notCreated)
-                && Objects.equal(this.notUpdated, other.notUpdated)
-                && Objects.equal(this.notDestroyed, other.notDestroyed);
-        }
-        return false;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesError.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesError.java
deleted file mode 100644
index 517baf6..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesError.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.collect.ImmutableList;
-
-public class SetMessagesError extends SetError {
-
-    public static SetMessagesError.Builder builder() {
-        return new Builder();
-    }
-    
-    public static class Builder extends SetError.Builder {
-        
-        private List<BlobId> attachmentsNotFound;
-
-        private Builder() {
-            super();
-            attachmentsNotFound = new ArrayList<>();
-        }
-
-        @Override
-        public Builder description(String description) {
-            return (Builder) super.description(description);
-        }
-        
-        @Override
-        public Builder properties(MessageProperty... properties) {
-            return (Builder) super.properties(properties);
-        }
-        
-        @Override
-        public Builder properties(Set<MessageProperty> properties) {
-            return (Builder) super.properties(properties);
-        }
-        
-        @Override
-        public Builder type(Type type) {
-            return (Builder) super.type(type);
-        }
-
-        public Builder attachmentsNotFound(BlobId... attachmentIds) {
-            return attachmentsNotFound(Arrays.asList(attachmentIds));
-        }
-        
-        public Builder attachmentsNotFound(List<BlobId> attachmentIds) {
-            this.attachmentsNotFound.addAll(attachmentIds);
-            return this;
-        }
-        
-        @Override
-        public SetError build() {
-            return new SetMessagesError(super.build(), ImmutableList.copyOf(attachmentsNotFound));
-        }
-    }
-
-    private ImmutableList<BlobId> attachmentsNotFound;
-    
-    public SetMessagesError(SetError setError, ImmutableList<BlobId> attachmentsNotFound) {
-        super(setError);
-        this.attachmentsNotFound = attachmentsNotFound;
-    }
-    
-    @JsonSerialize
-    public List<BlobId> getAttachmentsNotFound() {
-        return attachmentsNotFound;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesRequest.java
deleted file mode 100644
index 17cf2af..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesRequest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.draft.methods.UpdateMessagePatchConverter;
-import org.apache.james.jmap.draft.methods.ValueWithId.CreationMessageEntry;
-import org.apache.james.jmap.draft.methods.ValueWithId.MDNCreationEntry;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.mailbox.model.MessageId;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-
-@JsonDeserialize(builder = SetMessagesRequest.Builder.class)
-public class SetMessagesRequest implements JmapRequest {
-    private static final String ISSUER = "SetMessagesRequest";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private String accountId;
-        private String ifInState;
-        private HashMap<CreationMessageId, CreationMessage> create;
-        private HashMap<CreationMessageId, JmapMDN> sendMDN;
-        private ImmutableMap.Builder<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>> updatesProvider;
-
-        private ImmutableList.Builder<MessageId> destroy;
-
-        private Builder() {
-            create = new HashMap<>();
-            sendMDN = new HashMap<>();
-            updatesProvider = ImmutableMap.builder();
-            destroy = ImmutableList.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-            }
-            return this;
-        }
-
-        public Builder ifInState(String ifInState) {
-            if (ifInState != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "ifInState");
-            }
-            return this;
-        }
-
-        public Builder create(CreationMessageId creationMessageId, CreationMessage creation) {
-            this.create.put(creationMessageId, creation);
-            return this;
-        }
-
-        public Builder create(Map<CreationMessageId, CreationMessage> creations) {
-            this.create.putAll(creations);
-            return this;
-        }
-
-        public Builder sendMDN(CreationMessageId creationMessageId, JmapMDN mdn) {
-            this.sendMDN.put(creationMessageId, mdn);
-            return this;
-        }
-
-        public Builder sendMDN(Map<CreationMessageId, JmapMDN> mdns) {
-            this.sendMDN.putAll(mdns);
-            return this;
-        }
-
-        public Builder update(Map<MessageId, ObjectNode> updates) {
-            this.updatesProvider.putAll(Maps.transformValues(updates, json -> converter -> converter.fromJsonNode(json)));
-            return this;
-        }
-
-        public Builder destroy(List<MessageId> destroy) {
-            this.destroy.addAll(destroy);
-            return this;
-        }
-
-        public SetMessagesRequest build() {
-            return new SetMessagesRequest(Optional.ofNullable(accountId), Optional.ofNullable(ifInState), 
-                    messageCreations(), mdnSendings(), updatesProvider.build(), destroy.build());
-        }
-
-        private ImmutableList<CreationMessageEntry> messageCreations() {
-            return create.entrySet().stream()
-                    .map(entry -> new CreationMessageEntry(entry.getKey(), entry.getValue()))
-                    .collect(ImmutableList.toImmutableList());
-        }
-
-        private ImmutableList<MDNCreationEntry> mdnSendings() {
-            return sendMDN.entrySet().stream()
-                    .map(entry -> new MDNCreationEntry(entry.getKey(), entry.getValue()))
-                    .collect(ImmutableList.toImmutableList());
-        }
-    }
-
-    private final Optional<String> accountId;
-    private final Optional<String> ifInState;
-    private final List<CreationMessageEntry> create;
-    private final List<MDNCreationEntry> sendMDN;
-    private final Map<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>> update;
-    private final List<MessageId> destroy;
-
-    @VisibleForTesting SetMessagesRequest(Optional<String> accountId, Optional<String> ifInState,
-                                          List<CreationMessageEntry> create, List<MDNCreationEntry> sendMDN,
-                                          Map<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>> update,
-                                          List<MessageId> destroy) {
-        this.accountId = accountId;
-        this.ifInState = ifInState;
-        this.create = create;
-        this.sendMDN = sendMDN;
-        this.update = update;
-        this.destroy = destroy;
-    }
-
-    public Optional<String> getAccountId() {
-        return accountId;
-    }
-
-    public Optional<String> getIfInState() {
-        return ifInState;
-    }
-
-    public List<CreationMessageEntry> getCreate() {
-        return create;
-    }
-
-    public List<MDNCreationEntry> getSendMDN() {
-        return sendMDN;
-    }
-
-    public Map<MessageId, UpdateMessagePatch> buildUpdatePatches(UpdateMessagePatchConverter converter) {
-        return Maps.transformValues(update, func -> func.apply(converter));
-    }
-
-    public boolean hasUpdates() {
-        return !update.isEmpty();
-    }
-
-    public List<MessageId> getDestroy() {
-        return destroy;
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesResponse.java
deleted file mode 100644
index 5318cbc..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetMessagesResponse.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.mailbox.model.MessageId;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMessagesResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        public static Builder accumulator(Builder accumulator, SetMessagesResponse response) {
-            return response.mergeInto(accumulator);
-        }
-
-        public static Builder combiner(Builder firstBuilder, Builder secondBuilder) {
-            return secondBuilder.build().mergeInto(firstBuilder);
-        }
-
-        private String accountId;
-        private String oldState;
-        private String newState;
-        private final ImmutableMap.Builder<CreationMessageId, MessageFullView> created;
-        private final ImmutableMap.Builder<CreationMessageId, MessageId> mdnSent;
-        private final ImmutableList.Builder<MessageId> updated;
-        private final ImmutableList.Builder<MessageId> destroyed;
-        private final ImmutableMap.Builder<CreationMessageId, SetError> notCreated;
-        private final ImmutableMap.Builder<CreationMessageId, SetError> mdnNotSent;
-        private final ImmutableMap.Builder<MessageId, SetError> notUpdated;
-        private final ImmutableMap.Builder<MessageId, SetError> notDestroyed;
-
-        private Builder() {
-            created = ImmutableMap.builder();
-            mdnSent = ImmutableMap.builder();
-            updated = ImmutableList.builder();
-            destroyed = ImmutableList.builder();
-            notCreated = ImmutableMap.builder();
-            mdnNotSent = ImmutableMap.builder();
-            notUpdated = ImmutableMap.builder();
-            notDestroyed = ImmutableMap.builder();
-        }
-
-        public Builder accountId(String accountId) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder oldState(String oldState) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder newState(String newState) {
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder created(CreationMessageId creationMessageId, MessageFullView message) {
-            this.created.put(creationMessageId, message);
-            return this;
-        }
-
-        public Builder created(Map<CreationMessageId, MessageFullView> created) {
-            this.created.putAll(created);
-            return this;
-        }
-
-        public Builder mdnSent(CreationMessageId creationMessageId, MessageId messageId) {
-            this.mdnSent.put(creationMessageId, messageId);
-            return this;
-        }
-
-        public Builder mdnSent(ImmutableMap<CreationMessageId, MessageId> sent) {
-            this.mdnSent.putAll(sent);
-            return this;
-        }
-
-        public Builder updated(List<MessageId> updated) {
-            this.updated.addAll(updated);
-            return this;
-        }
-
-        public Builder destroyed(MessageId destroyed) {
-            this.destroyed.add(destroyed);
-            return this;
-        }
-
-        public Builder destroyed(List<MessageId> destroyed) {
-            this.destroyed.addAll(destroyed);
-            return this;
-        }
-
-        public Builder notCreated(Map<CreationMessageId, SetError> notCreated) {
-            this.notCreated.putAll(notCreated);
-            return this;
-        }
-
-        public Builder mdnNotSent(Map<CreationMessageId, SetError> notCreated) {
-            this.mdnNotSent.putAll(notCreated);
-            return this;
-        }
-
-        public Builder mdnNotSent(CreationMessageId creationMessageId, SetError error) {
-            this.mdnNotSent.put(creationMessageId, error);
-            return this;
-        }
-        
-        public Builder notCreated(CreationMessageId id, SetError error) {
-            this.notCreated.put(id, error);
-            return this;
-        }
-
-        public Builder notUpdated(Map<MessageId, SetError> notUpdated) {
-            this.notUpdated.putAll(notUpdated);
-            return this;
-        }
-
-        public Builder notUpdated(MessageId messageId, SetError error) {
-            this.notUpdated.put(messageId, error);
-            return this;
-        }
-
-        public Builder notDestroyed(MessageId messageId, SetError notDestroyed) {
-            this.notDestroyed.put(messageId, notDestroyed);
-            return this;
-        }
-
-        public Builder notDestroyed(Map<MessageId, SetError> notDestroyed) {
-            this.notDestroyed.putAll(notDestroyed);
-            return this;
-        }
-
-        public Builder mergeWith(Builder otherBuilder) {
-            return otherBuilder.build().mergeInto(this);
-        }
-
-        public SetMessagesResponse build() {
-            return new SetMessagesResponse(accountId, oldState, newState, 
-                created.build(), mdnSent.build(), updated.build(), destroyed.build(),
-                notCreated.build(), mdnNotSent.build(), notUpdated.build(), notDestroyed.build());
-        }
-    }
-
-    private final String accountId;
-    private final String oldState;
-    private final String newState;
-    private final ImmutableMap<CreationMessageId, MessageFullView> created;
-    private final ImmutableMap<CreationMessageId, MessageId> mdnSent;
-    private final ImmutableList<MessageId> updated;
-    private final ImmutableList<MessageId> destroyed;
-    private final ImmutableMap<CreationMessageId, SetError> notCreated;
-    private final ImmutableMap<CreationMessageId, SetError> mdnNotSent;
-    private final ImmutableMap<MessageId, SetError> notUpdated;
-    private final ImmutableMap<MessageId, SetError> notDestroyed;
-
-    @VisibleForTesting SetMessagesResponse(String accountId, String oldState, String newState, ImmutableMap<CreationMessageId, MessageFullView> created, ImmutableMap<CreationMessageId, MessageId> mdnSent, ImmutableList<MessageId> updated, ImmutableList<MessageId> destroyed,
-                                           ImmutableMap<CreationMessageId, SetError> notCreated, ImmutableMap<CreationMessageId, SetError> mdnNotSent, ImmutableMap<MessageId, SetError> notUpdated, ImmutableMap<MessageId, SetError> notDestroyed) {
-        this.accountId = accountId;
-        this.oldState = oldState;
-        this.newState = newState;
-        this.created = created;
-        this.mdnSent = mdnSent;
-        this.updated = updated;
-        this.destroyed = destroyed;
-        this.notCreated = notCreated;
-        this.mdnNotSent = mdnNotSent;
-        this.notUpdated = notUpdated;
-        this.notDestroyed = notDestroyed;
-    }
-
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public String getOldState() {
-        return oldState;
-    }
-
-    public String getNewState() {
-        return newState;
-    }
-
-    public ImmutableMap<CreationMessageId, MessageFullView> getCreated() {
-        return created;
-    }
-
-    public ImmutableList<MessageId> getUpdated() {
-        return updated;
-    }
-
-    public ImmutableList<MessageId> getDestroyed() {
-        return destroyed;
-    }
-
-    public ImmutableMap<CreationMessageId, SetError> getNotCreated() {
-        return notCreated;
-    }
-
-    public ImmutableMap<MessageId, SetError> getNotUpdated() {
-        return notUpdated;
-    }
-
-    public ImmutableMap<MessageId, SetError> getNotDestroyed() {
-        return notDestroyed;
-    }
-
-    @JsonProperty("MDNSent")
-    public ImmutableMap<CreationMessageId, MessageId> getMDNSent() {
-        return mdnSent;
-    }
-
-    @JsonProperty("MDNNotSent")
-    public ImmutableMap<CreationMessageId, SetError> getMDNNotSent() {
-        return mdnNotSent;
-    }
-
-    public SetMessagesResponse.Builder mergeInto(SetMessagesResponse.Builder responseBuilder) {
-        responseBuilder.created(getCreated());
-        responseBuilder.updated(getUpdated());
-        responseBuilder.destroyed(getDestroyed());
-        responseBuilder.notCreated(getNotCreated());
-        responseBuilder.notUpdated(getNotUpdated());
-        responseBuilder.notDestroyed(getNotDestroyed());
-        responseBuilder.mdnNotSent(getMDNNotSent());
-        responseBuilder.mdnSent(getMDNSent());
-        if (! Strings.isNullOrEmpty(getAccountId())) {
-            responseBuilder.accountId(getAccountId());
-        }
-        if (! Strings.isNullOrEmpty(getOldState())) {
-            responseBuilder.accountId(getOldState());
-        }
-        if (! Strings.isNullOrEmpty(getNewState())) {
-            responseBuilder.accountId(getAccountId());
-        }
-        return responseBuilder;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationRequest.java
deleted file mode 100644
index 5496e93..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationRequest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.Map;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.vacation.api.Vacation;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-
-@JsonDeserialize(builder = SetVacationRequest.Builder.class)
-public class SetVacationRequest implements JmapRequest {
-    private static final String ISSUER = "SetVacationRequest";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Map<String, VacationResponse> update = Maps.newHashMap();
-
-        public Builder accountId(String accountId) {
-            if (accountId != null) {
-                throw new JmapFieldNotSupportedException(ISSUER, "accountId");
-            }
-            return this;
-        }
-
-        public Builder update(Map<String, VacationResponse> update) {
-            this.update.putAll(update);
-            return this;
-        }
-
-        @JsonIgnore
-        public Builder update(String id, VacationResponse vacationResponse) {
-            this.update.put(id, vacationResponse);
-            return this;
-        }
-
-        public SetVacationRequest build() {
-            return new SetVacationRequest(ImmutableMap.copyOf(update));
-        }
-    }
-
-    private final Map<String, VacationResponse> update;
-
-    private SetVacationRequest(Map<String, VacationResponse> update) {
-        this.update = update;
-    }
-
-    public Map<String, VacationResponse> getUpdate() {
-        return update;
-    }
-
-    @JsonIgnore
-    public boolean isValid() {
-        return update.entrySet().size() == 1 && update.containsKey(Vacation.ID);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationResponse.java
deleted file mode 100644
index 03a464c..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SetVacationResponse.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.jmap.methods.Method;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetVacationResponse implements Method.Response {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        private Optional<String> updatedId = Optional.empty();
-        private Optional<String> notUpdatedId = Optional.empty();
-        private Optional<SetError> setError = Optional.empty();
-
-        public Builder updatedId(String updatedId) {
-            Preconditions.checkNotNull(updatedId);
-            this.updatedId = Optional.of(updatedId);
-            return this;
-        }
-
-        public Builder notUpdated(String id, SetError setError) {
-            Preconditions.checkNotNull(id);
-            Preconditions.checkNotNull(setError);
-            this.notUpdatedId = Optional.of(id);
-            this.setError = Optional.of(setError);
-            return this;
-        }
-
-        public SetVacationResponse build() {
-            return new SetVacationResponse(
-                updatedId.map(ImmutableList::of),
-                notUpdatedId.map(id -> ImmutableMap.of(id, setError.get())));
-        }
-    }
-
-    private final Optional<List<String>> updated;
-    private final Optional<Map<String, SetError>> notUpdated;
-
-    private SetVacationResponse(Optional<List<String>> updated, Optional<Map<String, SetError>> notUpdated) {
-        this.updated = updated;
-        this.notUpdated = notUpdated;
-    }
-
-    public Optional<List<String>> getUpdated() {
-        return updated;
-    }
-
-    public Optional<Map<String, SetError>> getNotUpdated() {
-        return notUpdated;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        SetVacationResponse that = (SetVacationResponse) o;
-
-        return Objects.equals(this.updated, that.updated)
-            && Objects.equals(this.notUpdated, that.notUpdated);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(updated, notUpdated);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SignedExpiringToken.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SignedExpiringToken.java
deleted file mode 100644
index f6db7e4..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/SignedExpiringToken.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-
-public interface SignedExpiringToken {
-
-    ZonedDateTime getExpirationDate();
-
-    String getSignedContent();
-
-    String getPayload();
-
-    String getSignature();
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UpdateMessagePatch.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UpdateMessagePatch.java
deleted file mode 100644
index 32938f0..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UpdateMessagePatch.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import jakarta.mail.Flags;
-
-import org.apache.james.jmap.draft.methods.ValidationResult;
-import org.apache.james.jmap.model.Keywords;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-
-@JsonDeserialize(builder = UpdateMessagePatch.Builder.class)
-public class UpdateMessagePatch {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private Optional<List<String>> mailboxIds = Optional.empty();
-        private OldKeyword.Builder oldKeyworkBuilder = OldKeyword.builder();
-        private Optional<Map<String, Boolean>> keywords = Optional.empty();
-        private Set<ValidationResult> validationResult = Sets.newHashSet();
-
-        public Builder mailboxIds(List<String> mailboxIds) {
-            this.mailboxIds = Optional.of(ImmutableList.copyOf(mailboxIds));
-            return this;
-        }
-
-        public Builder keywords(Map<String, Boolean> keywords) {
-            this.keywords = Optional.of(ImmutableMap.copyOf(keywords));
-            return this;
-        }
-
-        public Builder isFlagged(Boolean isFlagged) {
-            oldKeyworkBuilder.isFlagged(isFlagged);
-            return this;
-        }
-
-        public Builder isUnread(Boolean isUnread) {
-            oldKeyworkBuilder.isUnread(isUnread);
-            return this;
-        }
-
-        public Builder isAnswered(Boolean isAnswered) {
-            oldKeyworkBuilder.isAnswered(isAnswered);
-            return this;
-        }
-
-        public Builder isDraft(Boolean isDraft) {
-            oldKeyworkBuilder.isDraft(isDraft);
-            return this;
-        }
-
-        public Builder isForwarded(Boolean isForwarded) {
-            oldKeyworkBuilder.isForwarded(isForwarded);
-            return this;
-        }
-
-        public Builder validationResult(Set<ValidationResult> validationResult) {
-            this.validationResult.addAll(validationResult);
-            return this;
-        }
-
-        public UpdateMessagePatch build() {
-            if (mailboxIds.isPresent() && mailboxIds.get().isEmpty()) {
-                validationResult(ImmutableSet.of(ValidationResult.builder()
-                    .property("mailboxIds")
-                    .message("mailboxIds property is not supposed to be empty")
-                    .build()));
-            }
-
-            Optional<Keywords> mayBeKeywords = creationKeywords();
-            Optional<OldKeyword> oldKeywords = oldKeyworkBuilder.computeOldKeyword();
-            Preconditions.checkArgument(!(mayBeKeywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time");
-
-            return new UpdateMessagePatch(mailboxIds, mayBeKeywords, oldKeywords, ImmutableList.copyOf(validationResult));
-        }
-
-        public Optional<Keywords> creationKeywords() {
-            return keywords.map(map -> Keywords.strictFactory()
-                    .fromMap(map));
-        }
-
-    }
-
-    private final Optional<List<String>> mailboxIds;
-    private final Optional<Keywords> keywords;
-    private final Optional<OldKeyword> oldKeywords;
-    private final ImmutableList<ValidationResult> validationErrors;
-
-    @VisibleForTesting
-    UpdateMessagePatch(Optional<List<String>> mailboxIds,
-                       Optional<Keywords> keywords,
-                       Optional<OldKeyword> oldKeywords,
-                       ImmutableList<ValidationResult> validationResults) {
-
-        this.mailboxIds = mailboxIds;
-        this.keywords = keywords;
-        this.oldKeywords = oldKeywords;
-        this.validationErrors = validationResults;
-    }
-
-    public Optional<List<String>> getMailboxIds() {
-        return mailboxIds;
-    }
-
-    public boolean isFlagsIdentity() {
-        return !oldKeywords.isPresent() && !keywords.isPresent();
-    }
-
-    public boolean isOnlyAFlagUpdate() {
-        return !mailboxIds.isPresent() && (oldKeywords.isPresent() || keywords.isPresent());
-    }
-
-    public boolean isOnlyAMove() {
-        return mailboxIds.map(list -> list.size() == 1).orElse(false)
-            && oldKeywords.isEmpty()
-            && keywords.isEmpty();
-    }
-
-    public ImmutableList<ValidationResult> getValidationErrors() {
-        return validationErrors;
-    }
-
-    public boolean isValid() {
-        return getValidationErrors().isEmpty();
-    }
-
-    public Flags applyToState(Flags currentFlags) {
-        return oldKeywords
-            .map(oldKeyword -> oldKeyword.applyToState(currentFlags))
-            .orElse(keywords
-                .map(keyword -> keyword.asFlagsWithRecentAndDeletedFrom(currentFlags))
-                .orElse(currentFlags));
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof UpdateMessagePatch) {
-            UpdateMessagePatch that = (UpdateMessagePatch) o;
-
-            return Objects.equals(this.mailboxIds, that.mailboxIds)
-                && Objects.equals(this.keywords, that.keywords)
-                && Objects.equals(this.oldKeywords, that.oldKeywords)
-                && Objects.equals(this.validationErrors, that.validationErrors);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(mailboxIds, keywords, oldKeywords, validationErrors);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("mailboxIds", mailboxIds)
-            .add("keywords", keywords)
-            .add("oldKeywords", oldKeywords)
-            .add("validationErrors", validationErrors)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UploadResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UploadResponse.java
deleted file mode 100644
index bf40f58..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/UploadResponse.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-import org.apache.james.jmap.model.Number;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-
-@JsonDeserialize(builder = UploadResponse.Builder.class)
-public class UploadResponse {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private String accountId;
-        private String blobId;
-        private String type;
-        private Number size;
-        private ZonedDateTime expires;
-
-        public Builder accountId(String accountId) {
-            this.accountId = accountId;
-            return this;
-        }
-
-        public Builder blobId(String blobId) {
-            this.blobId = blobId;
-            return this;
-        }
-
-        public Builder type(String type) {
-            this.type = type;
-            return this;
-        }
-
-        public Builder size(long size) {
-            this.size = Number.BOUND_SANITIZING_FACTORY.from(size);
-            return this;
-        }
-
-        public Builder expires(ZonedDateTime expires) {
-            this.expires = expires;
-            return this;
-        }
-
-        public UploadResponse build() {
-            Preconditions.checkState(!Strings.isNullOrEmpty(blobId), "'blobId' is mandatory");
-            Preconditions.checkState(!Strings.isNullOrEmpty(type), "'type' is mandatory");
-            Preconditions.checkState(size != null, "'size' is mandatory");
-            return new UploadResponse(Optional.ofNullable(accountId), blobId, type, size, Optional.ofNullable(expires));
-        }
-    }
-
-    private final Optional<String> accountId;
-    private final String blobId;
-    private final String type;
-    private final Number size;
-    private final Optional<ZonedDateTime> expires;
-
-    @VisibleForTesting UploadResponse(Optional<String> accountId, String blobId, String type, Number size, Optional<ZonedDateTime> expires) {
-        this.accountId = accountId;
-        this.blobId = blobId;
-        this.type = type;
-        this.size = size;
-        this.expires = expires;
-    }
-
-    public Optional<String> getAccountId() {
-        return accountId;
-    }
-
-    public String getBlobId() {
-        return blobId;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public Number getSize() {
-        return size;
-    }
-
-    public Optional<ZonedDateTime> getExpires() {
-        return expires;
-    }
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof UploadResponse) {
-            UploadResponse other = (UploadResponse) obj;
-            return Objects.equal(accountId, other.accountId)
-                && Objects.equal(blobId, other.blobId)
-                && Objects.equal(type, other.type)
-                && Objects.equal(size, other.size)
-                && Objects.equal(expires, other.expires);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hashCode(accountId, blobId, type, size, expires);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects
-                .toStringHelper(this)
-                .add("accountId", accountId)
-                .add("blobId", blobId)
-                .add("type", type)
-                .add("size", size)
-                .add("expires", expires)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/VacationResponse.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/VacationResponse.java
deleted file mode 100644
index c168ac8..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/VacationResponse.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import java.time.ZonedDateTime;
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.json.OptionalZonedDateTimeDeserializer;
-import org.apache.james.jmap.draft.json.OptionalZonedDateTimeSerializer;
-import org.apache.james.util.ValuePatch;
-import org.apache.james.vacation.api.Vacation;
-import org.apache.james.vacation.api.VacationPatch;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-@JsonDeserialize(builder = VacationResponse.Builder.class)
-public class VacationResponse {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-        private ValuePatch<String> id = ValuePatch.keep();
-        private ValuePatch<Boolean> isEnabled = ValuePatch.keep();
-        private ValuePatch<ZonedDateTime> fromDate = ValuePatch.keep();
-        private ValuePatch<ZonedDateTime> toDate = ValuePatch.keep();
-        private ValuePatch<String> subject = ValuePatch.keep();
-        private ValuePatch<String> textBody = ValuePatch.keep();
-        private ValuePatch<String> htmlBody = ValuePatch.keep();
-        private Optional<Boolean> isActivated = Optional.empty();
-
-        public Builder id(String id) {
-            this.id = ValuePatch.modifyTo(id);
-            return this;
-        }
-
-        @JsonProperty("isEnabled")
-        public Builder enabled(boolean enabled) {
-            isEnabled = ValuePatch.modifyTo(enabled);
-            return this;
-        }
-
-        @JsonProperty("isActivated")
-        public Builder activated(Optional<Boolean> activated) {
-            Preconditions.checkNotNull(activated);
-            this.isActivated = activated;
-            return this;
-        }
-
-        @JsonIgnore
-        public Builder activated(boolean activated) {
-            return activated(Optional.of(activated));
-        }
-
-        @JsonDeserialize(using = OptionalZonedDateTimeDeserializer.class)
-        public Builder fromDate(Optional<ZonedDateTime> fromDate) {
-            this.fromDate = ValuePatch.ofOptional(fromDate);
-            return this;
-        }
-
-        @JsonDeserialize(using = OptionalZonedDateTimeDeserializer.class)
-        public Builder toDate(Optional<ZonedDateTime> toDate) {
-            this.toDate = ValuePatch.ofOptional(toDate);
-            return this;
-        }
-
-        public Builder textBody(Optional<String> textBody) {
-            this.textBody = ValuePatch.ofOptional(textBody);
-            return this;
-        }
-
-        public Builder subject(Optional<String> subject) {
-            this.subject = ValuePatch.ofOptional(subject);
-            return this;
-        }
-
-        public Builder htmlBody(Optional<String> htmlBody) {
-            this.htmlBody = ValuePatch.ofOptional(htmlBody);
-            return this;
-        }
-
-        public Builder fromVacation(Vacation vacation) {
-            this.id = ValuePatch.modifyTo(Vacation.ID);
-            this.isEnabled = ValuePatch.modifyTo(vacation.isEnabled());
-            this.fromDate = ValuePatch.ofOptional(vacation.getFromDate());
-            this.toDate = ValuePatch.ofOptional(vacation.getToDate());
-            this.textBody = ValuePatch.ofOptional(vacation.getTextBody());
-            this.subject = ValuePatch.ofOptional(vacation.getSubject());
-            this.htmlBody = ValuePatch.ofOptional(vacation.getHtmlBody());
-            return this;
-        }
-
-        public VacationResponse build() {
-            return new VacationResponse(id, isEnabled, fromDate, toDate, textBody, subject, htmlBody, isActivated);
-        }
-    }
-
-    private final ValuePatch<String> id;
-    private final ValuePatch<Boolean> isEnabled;
-    private final ValuePatch<ZonedDateTime> fromDate;
-    private final ValuePatch<ZonedDateTime> toDate;
-    private final ValuePatch<String> subject;
-    private final ValuePatch<String> textBody;
-    private final ValuePatch<String> htmlBody;
-    private final Optional<Boolean> isActivated;
-
-    private VacationResponse(ValuePatch<String> id, ValuePatch<Boolean> isEnabled, ValuePatch<ZonedDateTime> fromDate, ValuePatch<ZonedDateTime> toDate,
-                             ValuePatch<String> textBody, ValuePatch<String> subject, ValuePatch<String> htmlBody, Optional<Boolean> isActivated) {
-        this.id = id;
-        this.isEnabled = isEnabled;
-        this.fromDate = fromDate;
-        this.toDate = toDate;
-        this.textBody = textBody;
-        this.subject = subject;
-        this.htmlBody = htmlBody;
-        this.isActivated = isActivated;
-    }
-
-    public String getId() {
-        return id.get();
-    }
-
-    @JsonProperty("isEnabled")
-    public boolean isEnabled() {
-        return isEnabled.get();
-    }
-
-    @JsonSerialize(using = OptionalZonedDateTimeSerializer.class)
-    public Optional<ZonedDateTime> getFromDate() {
-        return fromDate.toOptional();
-    }
-
-    @JsonSerialize(using = OptionalZonedDateTimeSerializer.class)
-    public Optional<ZonedDateTime> getToDate() {
-        return toDate.toOptional();
-    }
-
-    public Optional<String> getTextBody() {
-        return textBody.toOptional();
-    }
-
-    public Optional<String> getSubject() {
-        return subject.toOptional();
-    }
-
-    public Optional<String> getHtmlBody() {
-        return htmlBody.toOptional();
-    }
-
-    @JsonIgnore
-    public boolean isValid() {
-        return isMissingOrGoodValue() && !isEnabled.isRemoved();
-    }
-
-    @JsonIgnore
-    private boolean isMissingOrGoodValue() {
-        return id.isKept() || id.toOptional().equals(Optional.of(Vacation.ID));
-    }
-
-    @JsonIgnore
-    public VacationPatch getPatch() {
-        return VacationPatch.builder()
-            .fromDate(fromDate)
-            .toDate(toDate)
-            .htmlBody(htmlBody)
-            .textBody(textBody)
-            .subject(subject)
-            .isEnabled(isEnabled)
-            .build();
-    }
-
-    @JsonProperty("isActivated")
-    public Optional<Boolean> isActivated() {
-        return isActivated;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        VacationResponse that = (VacationResponse) o;
-
-        return Objects.equals(this.id, that.id)
-            && Objects.equals(this.isEnabled, that.isEnabled)
-            && Objects.equals(this.fromDate, that.fromDate)
-            && Objects.equals(this.toDate, that.toDate)
-            && Objects.equals(this.textBody, that.textBody)
-            && Objects.equals(this.subject, that.subject)
-            && Objects.equals(this.htmlBody, that.htmlBody)
-            && Objects.equals(this.isActivated, that.isActivated);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, isEnabled, fromDate, toDate, textBody, subject, htmlBody, isActivated);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("id", id)
-            .add("fromDate", fromDate)
-            .add("toDate", toDate)
-            .add("isActivated", isActivated)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/deserialization/JmapRuleDTODeserializer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/deserialization/JmapRuleDTODeserializer.java
deleted file mode 100644
index e0d8bb4..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/deserialization/JmapRuleDTODeserializer.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.deserialization;
-
-import java.io.IOException;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.james.jmap.draft.model.JmapRuleDTO;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.base.Preconditions;
-
-public class JmapRuleDTODeserializer extends JsonDeserializer<JmapRuleDTO> {
-
-    @Override
-    public JmapRuleDTO deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException {
-        JsonNode node = jp.readValueAsTree();
-
-        JsonNode idNode = node.get("id");
-        Preconditions.checkArgument(!idNode.isNull(), "`id` is mandatory");
-        Preconditions.checkArgument(StringUtils.isNotBlank(idNode.asText()), "`id` is mandatory");
-
-        JsonNode nameNode = node.get("name");
-        Preconditions.checkArgument(!nameNode.isNull(), "`name` is mandatory");
-        Preconditions.checkArgument(StringUtils.isNotBlank(nameNode.asText()), "`name` is mandatory");
-
-        JsonNode conditionNode = node.get("condition");
-        Preconditions.checkArgument(!conditionNode.isNull(), "`condition` is mandatory");
-        JmapRuleDTO.ConditionDTO conditionDTO = jp.getCodec().treeToValue(conditionNode, JmapRuleDTO.ConditionDTO.class);
-
-        JsonNode actionNode = node.get("action");
-        Preconditions.checkArgument(!actionNode.isNull(), "`action` is mandatory");
-        JmapRuleDTO.ActionDTO actionDTO = jp.getCodec().treeToValue(actionNode, JmapRuleDTO.ActionDTO.class);
-
-        return new JmapRuleDTO(node.get("id").asText(), node.get("name").asText(), conditionDTO, actionDTO);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Mailbox.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Mailbox.java
deleted file mode 100644
index 2609827..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Mailbox.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.mailbox;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.jmap.methods.JmapResponseWriterImpl;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.jmap.model.mailbox.Rights;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.fasterxml.jackson.annotation.JsonFilter;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-
-@JsonDeserialize(builder = Mailbox.Builder.class)
-@JsonFilter(JmapResponseWriterImpl.PROPERTIES_FILTER)
-public class Mailbox {
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private MailboxId id;
-        private String name;
-        private Optional<MailboxId> parentId;
-        private Optional<Role> role;
-        private SortOrder sortOrder;
-        private boolean mustBeOnlyMailbox;
-        private boolean mayReadItems;
-        private boolean mayAddItems;
-        private boolean mayRemoveItems;
-        private boolean mayCreateChild;
-        private boolean mayRename;
-        private boolean mayDelete;
-        private Optional<Number> totalMessages;
-        private Optional<Number> unreadMessages;
-        private Optional<Number> totalThreads;
-        private Optional<Number> unreadThreads;
-        private Optional<Rights> sharedWith;
-        private Optional<MailboxNamespace> namespace;
-        private Optional<Quotas> quotas;
-
-        private Builder() {
-            parentId = Optional.empty();
-            sharedWith = Optional.empty();
-            namespace = Optional.empty();
-            totalMessages = Optional.empty();
-            unreadMessages = Optional.empty();
-            totalThreads = Optional.empty();
-            unreadThreads = Optional.empty();
-            role = Optional.empty();
-            quotas = Optional.empty();
-        }
-
-        public Builder id(MailboxId id) {
-            Preconditions.checkNotNull(id);
-            this.id = id;
-            return this;
-        }
-
-        public Builder name(String name) {
-            Preconditions.checkNotNull(name);
-            this.name = name;
-            return this;
-        }
-
-        public Builder parentId(MailboxId parentId) {
-            this.parentId = Optional.ofNullable(parentId);
-            return this;
-        }
-
-        public Builder role(Optional<Role> role) {
-            this.role = role;
-            return this;
-        }
-
-        public Builder sortOrder(SortOrder sortOrder) {
-            this.sortOrder = sortOrder;
-            return this;
-        }
-
-        public Builder mustBeOnlyMailbox(boolean mustBeOnlyMailbox) {
-            this.mustBeOnlyMailbox = mustBeOnlyMailbox;
-            return this;
-        }
-
-        public Builder mayReadItems(boolean mayReadItems) {
-            this.mayReadItems = mayReadItems;
-            return this;
-        }
-
-        public Builder mayAddItems(boolean mayAddItems) {
-            this.mayAddItems = mayAddItems;
-            return this;
-        }
-
-        public Builder mayRemoveItems(boolean mayRemoveItems) {
-            this.mayRemoveItems = mayRemoveItems;
-            return this;
-        }
-
-        public Builder mayCreateChild(boolean mayCreateChild) {
-            this.mayCreateChild = mayCreateChild;
-            return this;
-        }
-
-        public Builder mayRename(boolean mayRename) {
-            this.mayRename = mayRename;
-            return this;
-        }
-
-        public Builder mayDelete(boolean mayDelete) {
-            this.mayDelete = mayDelete;
-            return this;
-        }
-
-        public Builder totalMessages(long totalMessages) {
-            this.totalMessages = Optional.of(Number.BOUND_SANITIZING_FACTORY.from(totalMessages));
-            return this;
-        }
-
-        public Builder unreadMessages(long unreadMessages) {
-            this.unreadMessages = Optional.of(Number.BOUND_SANITIZING_FACTORY.from(unreadMessages));
-            return this;
-        }
-
-        public Builder totalThreads(long totalThreads) {
-            this.totalThreads = Optional.of(Number.BOUND_SANITIZING_FACTORY.from(totalThreads));
-            return this;
-        }
-
-        public Builder unreadThreads(long unreadThreads) {
-            this.unreadThreads = Optional.of(Number.BOUND_SANITIZING_FACTORY.from(unreadThreads));
-            return this;
-        }
-
-        public Builder sharedWith(Rights sharedWith) {
-            this.sharedWith = Optional.of(sharedWith);
-            return this;
-        }
-
-        public Builder namespace(MailboxNamespace namespace) {
-            this.namespace = Optional.of(namespace);
-            return this;
-        }
-
-        public Builder quotas(Quotas quotas) {
-            this.quotas = Optional.of(quotas);
-            return this;
-        }
-
-        public Mailbox build() {
-            Preconditions.checkState(!Strings.isNullOrEmpty(name), "'name' is mandatory");
-            Preconditions.checkState(id != null, "'id' is mandatory");
-
-            return new Mailbox(id,
-                name,
-                parentId,
-                role,
-                sortOrder,
-                mustBeOnlyMailbox,
-                mayReadItems,
-                mayAddItems,
-                mayRemoveItems,
-                mayCreateChild,
-                mayRename,
-                mayDelete,
-                totalMessages.orElse(Number.ZERO),
-                unreadMessages.orElse(Number.ZERO),
-                totalThreads.orElse(Number.ZERO),
-                unreadThreads.orElse(Number.ZERO),
-                sharedWith.orElse(Rights.EMPTY),
-                namespace.orElse(MailboxNamespace.personal()),
-                quotas);
-        }
-    }
-
-    private final MailboxId id;
-    private final String name;
-    private final Optional<MailboxId> parentId;
-    private final Optional<Role> role;
-    private final SortOrder sortOrder;
-    private final boolean mustBeOnlyMailbox;
-    private final boolean mayReadItems;
-    private final boolean mayAddItems;
-    private final boolean mayRemoveItems;
-    private final boolean mayCreateChild;
-    private final boolean mayRename;
-    private final boolean mayDelete;
-    private final Number totalMessages;
-    private final Number unreadMessages;
-    private final Number totalThreads;
-    private final Number unreadThreads;
-    private final Rights sharedWith;
-    private final MailboxNamespace namespace;
-    private final Optional<Quotas> quotas;
-
-    @VisibleForTesting Mailbox(MailboxId id, String name, Optional<MailboxId> parentId, Optional<Role> role, SortOrder sortOrder, boolean mustBeOnlyMailbox,
-                               boolean mayReadItems, boolean mayAddItems, boolean mayRemoveItems, boolean mayCreateChild, boolean mayRename, boolean mayDelete,
-                               Number totalMessages, Number unreadMessages, Number totalThreads, Number unreadThreads, Rights sharedWith, MailboxNamespace namespace,
-                               Optional<Quotas> quotas) {
-
-        this.id = id;
-        this.name = name;
-        this.parentId = parentId;
-        this.role = role;
-        this.sortOrder = sortOrder;
-        this.mustBeOnlyMailbox = mustBeOnlyMailbox;
-        this.mayReadItems = mayReadItems;
-        this.mayAddItems = mayAddItems;
-        this.mayRemoveItems = mayRemoveItems;
-        this.mayCreateChild = mayCreateChild;
-        this.mayRename = mayRename;
-        this.mayDelete = mayDelete;
-        this.totalMessages = totalMessages;
-        this.unreadMessages = unreadMessages;
-        this.totalThreads = totalThreads;
-        this.unreadThreads = unreadThreads;
-        this.sharedWith = sharedWith;
-        this.namespace = namespace;
-        this.quotas = quotas;
-    }
-
-    public MailboxId getId() {
-        return id;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Optional<MailboxId> getParentId() {
-        return parentId;
-    }
-
-    public Optional<Role> getRole() {
-        return role;
-    }
-
-    public SortOrder getSortOrder() {
-        return sortOrder;
-    }
-
-    public boolean isMustBeOnlyMailbox() {
-        return mustBeOnlyMailbox;
-    }
-
-    public boolean isMayReadItems() {
-        return mayReadItems;
-    }
-
-    public boolean isMayAddItems() {
-        return mayAddItems;
-    }
-
-    public boolean isMayRemoveItems() {
-        return mayRemoveItems;
-    }
-
-    public boolean isMayCreateChild() {
-        return mayCreateChild;
-    }
-
-    public boolean isMayRename() {
-        return mayRename;
-    }
-
-    public boolean isMayDelete() {
-        return mayDelete;
-    }
-
-    public Number getTotalMessages() {
-        return totalMessages;
-    }
-
-    public Number getUnreadMessages() {
-        return unreadMessages;
-    }
-
-    public Number getTotalThreads() {
-        return totalThreads;
-    }
-
-    public Number getUnreadThreads() {
-        return unreadThreads;
-    }
-
-    public Rights getSharedWith() {
-        return sharedWith;
-    }
-
-    public MailboxNamespace getNamespace() {
-        return namespace;
-    }
-
-    public Optional<Quotas> getQuotas() {
-        return quotas;
-    }
-
-    @JsonIgnore
-    public boolean hasRole(Role role) {
-        return this.role
-            .map(currentRole -> Objects.equals(currentRole, role))
-            .orElse(false);
-    }
-
-    @JsonIgnore
-    public boolean hasSystemRole() {
-        return role.map(Role::isSystemRole).orElse(false);
-    }
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof Mailbox) {
-            Mailbox other = (Mailbox) obj;
-            return Objects.equals(this.id, other.id)
-                && Objects.equals(this.name, other.name)
-                && Objects.equals(this.parentId, other.parentId)
-                && Objects.equals(this.role, other.role)
-                && Objects.equals(this.sortOrder, other.sortOrder)
-                && Objects.equals(this.mustBeOnlyMailbox, other.mustBeOnlyMailbox)
-                && Objects.equals(this.mayReadItems, other.mayReadItems)
-                && Objects.equals(this.mayAddItems, other.mayAddItems)
-                && Objects.equals(this.mayRemoveItems, other.mayRemoveItems)
-                && Objects.equals(this.mayCreateChild, other.mayCreateChild)
-                && Objects.equals(this.mayRename, other.mayRename)
-                && Objects.equals(this.mayDelete, other.mayDelete)
-                && Objects.equals(this.totalMessages, other.totalMessages)
-                && Objects.equals(this.unreadMessages, other.unreadMessages)
-                && Objects.equals(this.totalThreads, other.totalThreads)
-                && Objects.equals(this.unreadThreads, other.unreadThreads)
-                && Objects.equals(this.sharedWith, other.sharedWith)
-                && Objects.equals(this.namespace, other.namespace)
-                && Objects.equals(this.quotas, other.quotas);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(id, name, parentId, role, sortOrder, mustBeOnlyMailbox, mayReadItems, mayAddItems, 
-            mayRemoveItems, mayCreateChild, mayRename, mayDelete, totalMessages, unreadMessages, totalThreads,
-            unreadThreads, sharedWith, namespace, quotas);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("id", id)
-                .add("name", name)
-                .add("sortOrder", sortOrder)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequest.java
deleted file mode 100644
index 893aa5b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/****************************************************************
-O * 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.draft.model.mailbox;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.exceptions.JmapFieldNotSupportedException;
-import org.apache.james.jmap.draft.model.MailboxCreationId;
-import org.apache.james.mailbox.Role;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-
-@JsonDeserialize(builder = MailboxCreateRequest.Builder.class)
-public class MailboxCreateRequest {
-    private static final String ISSUER = "MailboxCreateRequest";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Optional<String> id;
-        private String name;
-        private Optional<MailboxCreationId> parentId;
-        private Optional<Role> role;
-        private Optional<SortOrder> sortOrder;
-
-        private Builder() {
-            id = Optional.empty();
-            role = Optional.empty();
-            sortOrder = Optional.empty();
-            parentId = Optional.empty();
-        }
-
-        public Builder id(String id) {
-            Preconditions.checkNotNull(id);
-            this.id = Optional.of(id);
-            return this;
-        }
-
-        public Builder name(String name) {
-            Preconditions.checkNotNull(name);
-            this.name = name;
-            return this;
-        }
-
-        public Builder parentId(MailboxCreationId parentId) {
-            Preconditions.checkNotNull(parentId);
-            this.parentId = Optional.of(parentId);
-            return this;
-        }
-
-        public Builder role(Role role) {
-            Preconditions.checkNotNull(role);
-            throw new JmapFieldNotSupportedException(ISSUER, "role");
-        }
-
-        public Builder sortOrder(SortOrder sortOrder) {
-            Preconditions.checkNotNull(sortOrder);
-            throw new JmapFieldNotSupportedException(ISSUER, "sortOrder");
-        }
-
-
-        public MailboxCreateRequest build() {
-            Preconditions.checkState(!Strings.isNullOrEmpty(name), "'name' is mandatory");
-            return new MailboxCreateRequest(id, name, parentId, role, sortOrder);
-        }
-    }
-
-    private final Optional<String> id;
-    private final String name;
-    private final Optional<MailboxCreationId> parentId;
-    private final Optional<Role> role;
-    private final Optional<SortOrder> sortOrder;
-
-    @VisibleForTesting
-    MailboxCreateRequest(Optional<String> id, String name, Optional<MailboxCreationId> parentId, Optional<Role> role, Optional<SortOrder> sortOrder) {
-
-        this.id = id;
-        this.name = name;
-        this.parentId = parentId;
-        this.role = role;
-        this.sortOrder = sortOrder;
-    }
-
-    public Optional<String> getId() {
-        return id;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Optional<MailboxCreationId> getParentId() {
-        return parentId;
-    }
-
-    public Optional<Role> getRole() {
-        return role;
-    }
-
-    public Optional<SortOrder> getSortOrder() {
-        return sortOrder;
-    }
-
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof MailboxCreateRequest) {
-            MailboxCreateRequest other = (MailboxCreateRequest) obj;
-            return Objects.equals(this.id, other.id)
-                && Objects.equals(this.name, other.name)
-                && Objects.equals(this.parentId, other.parentId)
-                && Objects.equals(this.role, other.role)
-                && Objects.equals(this.sortOrder, other.sortOrder);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(id, name, parentId, role, sortOrder);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("id", id)
-                .add("name", name)
-                .add("parentId", parentId)
-                .add("role", role)
-                .add("sortOrder", sortOrder)
-                .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespace.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespace.java
deleted file mode 100644
index 38e1a15..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespace.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.mailbox;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.core.Username;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-public class MailboxNamespace {
-    public enum Type {
-        Delegated("Delegated"),
-        Personal("Personal");
-
-        @SuppressWarnings("unused")
-        private final String type;
-
-        Type(String type) {
-            this.type = type;
-        }
-    }
-
-    public static MailboxNamespace delegated(Username owner) {
-        Preconditions.checkArgument(owner != null);
-
-        return new MailboxNamespace(Type.Delegated, Optional.of(owner));
-    }
-
-    public static MailboxNamespace personal() {
-        return new MailboxNamespace(Type.Personal, Optional.empty());
-    }
-
-    private final Type type;
-    private final Optional<Username> owner;
-
-    private MailboxNamespace(Type type, Optional<Username> owner) {
-        this.type = type;
-        this.owner = owner;
-    }
-
-    public Type getType() {
-        return type;
-    }
-
-    @JsonInclude(JsonInclude.Include.NON_EMPTY)
-    public Optional<Username> getOwner() {
-        return owner;
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof MailboxNamespace) {
-            MailboxNamespace that = (MailboxNamespace) o;
-
-            return Objects.equals(this.type, that.type) &&
-                Objects.equals(this.owner, that.owner);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(type, owner);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("type", type)
-            .add("owner", owner)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequest.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequest.java
deleted file mode 100644
index 450dbec..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequest.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/****************************************************************
-O * 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.draft.model.mailbox;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.model.mailbox.Rights;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-@JsonDeserialize(builder = MailboxUpdateRequest.Builder.class)
-public class MailboxUpdateRequest {
-
-    public static Builder builder() throws MailboxException {
-        return new Builder();
-    }
-
-    @JsonPOJOBuilder(withPrefix = "")
-    public static class Builder {
-
-        private Optional<String> name;
-        private Optional<MailboxId> parentId;
-        private Optional<Role> role;
-        private Optional<SortOrder> sortOrder;
-        private Optional<Rights> sharedWith;
-
-        private Builder() {
-            name = Optional.empty();
-            role = Optional.empty();
-            sortOrder = Optional.empty();
-            parentId = Optional.empty();
-            sharedWith = Optional.empty();
-        }
-
-        public Builder name(String name) throws MailboxException {
-            Preconditions.checkNotNull(name);
-            Preconditions.checkArgument(!name.isEmpty());
-            this.name = Optional.of(name);
-            return this;
-        }
-
-        public Builder parentId(MailboxId parentId) {
-            if (parentId == null) {
-                this.parentId = null;
-            } else {
-                this.parentId = Optional.of(parentId);
-            }
-            return this;
-        }
-
-        public Builder role(Role role) {
-            Preconditions.checkNotNull(role);
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder sortOrder(SortOrder sortOrder) {
-            Preconditions.checkNotNull(sortOrder);
-            throw new NotImplementedException("not implemented");
-        }
-
-        public Builder sharedWith(Rights rights) {
-            Preconditions.checkNotNull(rights);
-            this.sharedWith = Optional.of(rights);
-            return this;
-        }
-
-        public MailboxUpdateRequest build() {
-            return new MailboxUpdateRequest(name, parentId, role, sortOrder, sharedWith);
-        }
-    }
-
-    private final Optional<String> name;
-    private final Optional<MailboxId> parentId;
-    private final Optional<Role> role;
-    private final Optional<SortOrder> sortOrder;
-    private final Optional<Rights> sharedWith;
-
-    @VisibleForTesting
-    MailboxUpdateRequest(Optional<String> name, Optional<MailboxId> parentId, Optional<Role> role, Optional<SortOrder> sortOrder, Optional<Rights> sharedWith) {
-
-        this.name = name;
-        this.parentId = parentId;
-        this.role = role;
-        this.sortOrder = sortOrder;
-        this.sharedWith = sharedWith;
-    }
-
-    public Optional<String> getName() {
-        return name;
-    }
-
-    public Optional<MailboxId> getParentId() {
-        return parentId;
-    }
-
-    public Optional<Role> getRole() {
-        return role;
-    }
-
-    public Optional<SortOrder> getSortOrder() {
-        return sortOrder;
-    }
-
-    public Optional<Rights> getSharedWith() {
-        return sharedWith;
-    }
-
-    @Override
-    public final boolean equals(Object obj) {
-        if (obj instanceof MailboxUpdateRequest) {
-            MailboxUpdateRequest other = (MailboxUpdateRequest) obj;
-            return Objects.equals(this.name, other.name)
-                && Objects.equals(this.parentId, other.parentId)
-                && Objects.equals(this.role, other.role)
-                && Objects.equals(this.sortOrder, other.sortOrder)
-                && Objects.equals(this.sharedWith, other.sharedWith);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(name, parentId, role, sortOrder, sharedWith);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-            .add("name", name)
-            .add("parentId", parentId)
-            .add("role", role)
-            .add("sortOrder", sortOrder)
-            .add("sharedWith", sharedWith)
-            .toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Quotas.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Quotas.java
deleted file mode 100644
index 71f0b99..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/Quotas.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model.mailbox;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.james.core.quota.QuotaCountLimit;
-import org.apache.james.core.quota.QuotaCountUsage;
-import org.apache.james.core.quota.QuotaLimitValue;
-import org.apache.james.core.quota.QuotaSizeLimit;
-import org.apache.james.core.quota.QuotaSizeUsage;
-import org.apache.james.core.quota.QuotaUsageValue;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.mailbox.model.QuotaRoot;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.collect.ImmutableMap;
-
-public class Quotas {
-
-    private final Map<QuotaId, Quota> quotas;
-
-    public static Quotas from(ImmutableMap<QuotaId, Quota> quotas) {
-        return new Quotas(quotas);
-    }
-
-    public static Quotas from(QuotaId quotaId, Quota quota) {
-        return new Quotas(ImmutableMap.of(quotaId, quota));
-    }
-
-    private Quotas(ImmutableMap<QuotaId, Quota> quotas) {
-        this.quotas = quotas;
-    }
-
-    @JsonValue
-    public Map<QuotaId, Quota> getQuotas() {
-        return quotas;
-    }
-
-    public static class QuotaId {
-        private final QuotaRoot quotaRoot;
-
-        public static QuotaId fromQuotaRoot(QuotaRoot quotaRoot) {
-            return new QuotaId(quotaRoot);
-        }
-        
-        private QuotaId(QuotaRoot quotaRoot) {
-            this.quotaRoot = quotaRoot;
-        }
-        
-        @JsonValue
-        public String getName() {
-            return quotaRoot.getValue();
-        }
-
-        @Override
-        public final boolean equals(Object o) {
-            if (o instanceof QuotaId) {
-                QuotaId other = (QuotaId) o;
-                return Objects.equals(quotaRoot, other.quotaRoot);
-            }
-            return false;
-        }
-
-        @Override
-        public final int hashCode() {
-            return Objects.hashCode(quotaRoot);
-        }
-    }
-
-    public static class Quota {
-        private final Map<Type, Value<?, ?>> quota;
-
-        public static Quota from(ImmutableMap<Type, Value<?, ?>> quota) {
-            return new Quota(quota);
-        }
-
-        public static Quota from(Value<QuotaSizeLimit, QuotaSizeUsage> storage, Value<QuotaCountLimit, QuotaCountUsage> message) {
-            return new Quota(ImmutableMap.of(Type.STORAGE, storage,
-                Type.MESSAGE, message));
-        }
-
-        private Quota(ImmutableMap<Type, Value<?, ?>> quota) {
-            this.quota = quota;
-        }
-
-        @JsonValue
-        public Map<Type, Value<?, ?>> getQuota() {
-            return quota;
-        }
-    }
-
-    public static enum Type {
-        STORAGE,
-        MESSAGE;
-    }
-
-    public static class Value<T extends QuotaLimitValue<T>, U extends QuotaUsageValue<U, T>> {
-        private final Number used;
-        private final Optional<Number> max;
-        
-        public Value(Number used, Optional<Number> max) {
-            this.used = used;
-            this.max = max;
-        }
-
-        public Number getUsed() {
-            return used;
-        }
-
-        public Optional<Number> getMax() {
-            return max;
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/SortOrder.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/SortOrder.java
deleted file mode 100644
index 84a29a9..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/mailbox/SortOrder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model.mailbox;
-
-import java.util.Optional;
-
-import org.apache.james.mailbox.Role;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-
-public class SortOrder implements Comparable<SortOrder> {
-
-    private static final SortOrder DEFAULT_SORT_ORDER = SortOrder.of(1000);
-    private static final ImmutableMap<Role, SortOrder> defaultSortOrders =
-            ImmutableMap.<Role, SortOrder>builder()
-                .put(Role.INBOX, SortOrder.of(10))
-                .put(Role.ARCHIVE, SortOrder.of(20))
-                .put(Role.DRAFTS, SortOrder.of(30))
-                .put(Role.OUTBOX, SortOrder.of(40))
-                .put(Role.SENT, SortOrder.of(50))
-                .put(Role.TRASH, SortOrder.of(60))
-                .put(Role.SPAM, SortOrder.of(70))
-                .put(Role.TEMPLATES, SortOrder.of(80))
-                .put(Role.RESTORED_MESSAGES, SortOrder.of(90))
-                .build();
-
-    private static Optional<SortOrder> getDefaultSortOrder(Role role) {
-        return Optional.ofNullable(defaultSortOrders.get(role));
-    }
-
-    public static SortOrder getSortOrder(Optional<Role> role) {
-        return role
-                .map(SortOrder::getDefaultSortOrder)
-                .map(Optional::get)
-                .orElse(DEFAULT_SORT_ORDER);
-    }
-
-    public static SortOrder of(int sortOrder) {
-        Preconditions.checkArgument(sortOrder >= 0, "'sortOrder' must be positive");
-        return new SortOrder(sortOrder);
-    }
-
-    private final int sortOrder;
-
-    private SortOrder(int sortOrder) {
-        this.sortOrder = sortOrder;
-    }
-
-    @JsonValue
-    public int getSortOrder() {
-        return sortOrder;
-    }
-
-    @Override
-    public int compareTo(SortOrder o) {
-        return Integer.compare(sortOrder, o.sortOrder);
-    }
-    
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass()).add("order", sortOrder).toString();
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof SortOrder) {
-            return sortOrder == ((SortOrder)obj).sortOrder;
-        }
-        return super.equals(obj);
-    }
-    
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(sortOrder);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/MailSpool.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/MailSpool.java
deleted file mode 100644
index e3f2db4..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/MailSpool.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.send;
-
-import java.io.IOException;
-
-import jakarta.annotation.PreDestroy;
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.send.MailMetadata;
-import org.apache.james.lifecycle.api.Disposable;
-import org.apache.james.lifecycle.api.Startable;
-import org.apache.james.queue.api.MailQueue;
-import org.apache.james.queue.api.MailQueueFactory;
-import org.apache.mailet.Attribute;
-import org.apache.mailet.AttributeValue;
-import org.apache.mailet.Mail;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import reactor.core.publisher.Mono;
-
-public class MailSpool implements Startable, Disposable {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(MailSpool.class);
-
-    private final MailQueueFactory<?> queueFactory;
-    private MailQueue queue;
-
-    @Inject
-    @VisibleForTesting MailSpool(MailQueueFactory<?> queueFactory) {
-        this.queueFactory = queueFactory;
-    }
-
-    public void start() {
-        queue = queueFactory.createQueue(MailQueueFactory.SPOOL);
-    }
-
-    @PreDestroy
-    public void dispose() {
-        try {
-            queue.close();
-        } catch (IOException e) {
-            LOGGER.debug("error closing queue", e);
-        }
-    }
-
-    public Mono<Void> send(Mail mail, MailMetadata metadata) {
-        mail.setAttribute(new Attribute(MailMetadata.MAIL_METADATA_MESSAGE_ID_ATTRIBUTE, AttributeValue.of(metadata.getMessageId().serialize())));
-        mail.setAttribute(new Attribute(MailMetadata.MAIL_METADATA_USERNAME_ATTRIBUTE, AttributeValue.of(metadata.getUsername())));
-        return Mono.from(queue.enqueueReactive(mail));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/exception/MessageIdNotFoundException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/exception/MessageIdNotFoundException.java
deleted file mode 100644
index a4e0c5f..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/exception/MessageIdNotFoundException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.send.exception;
-
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.queue.api.MailQueue.MailQueueException;
-
-public class MessageIdNotFoundException extends MailQueueException {
-
-    public MessageIdNotFoundException(MessageId messageId) {
-        super("Unable to find a Mailbox Message matching: " + messageId.serialize());
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DependencyGraph.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DependencyGraph.java
deleted file mode 100644
index 59a6069..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DependencyGraph.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Stream;
-
-import org.apache.james.util.streams.Iterators;
-import org.jgrapht.alg.cycle.CycleDetector;
-import org.jgrapht.graph.DefaultDirectedGraph;
-import org.jgrapht.graph.DefaultEdge;
-import org.jgrapht.graph.builder.GraphBuilder;
-import org.jgrapht.traverse.TopologicalOrderIterator;
-
-public class DependencyGraph<T> {
-
-    private final GraphBuilder<T, DefaultEdge, DefaultDirectedGraph<T, DefaultEdge>> builder;
-    private final Function<T, Optional<T>> getParent;
-
-    public DependencyGraph(Function<T, Optional<T>> getParent) {
-        this.getParent = getParent;
-        this.builder = new GraphBuilder<>(new DefaultDirectedGraph<>(DefaultEdge.class));
-    }
-
-    public void registerItem(T item) {
-        builder.addVertex(item);
-        getParent.apply(item)
-                .map(parentNode -> builder.addEdge(parentNode, item));
-    }
-
-    public Stream<T> getBuildChain() throws CycleDetectedException {
-        DefaultDirectedGraph<T, DefaultEdge> graph = builder.build();
-        ensureNoCycle(graph);
-        return Iterators.toStream(new TopologicalOrderIterator<>(graph));
-    }
-
-    private void ensureNoCycle(DefaultDirectedGraph<T, DefaultEdge> graph) throws CycleDetectedException {
-        CycleDetector<T, DefaultEdge> cycleDetector = new CycleDetector<>(graph);
-        if (cycleDetector.detectCycles()) {
-            throw new CycleDetectedException();
-        }
-    }
-
-    public String toString() {
-        return builder.build().toString();
-    }
-    
-    public static class CycleDetectedException extends RuntimeException {
-        
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DownloadPath.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DownloadPath.java
deleted file mode 100644
index 29a4e61..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/DownloadPath.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import java.util.Optional;
-
-public class DownloadPath {
-    public static DownloadPath ofBlobId(String blobId) {
-        return new DownloadPath(blobId, Optional.empty());
-    }
-
-    public static DownloadPath of(String blobId, String name) {
-        return new DownloadPath(blobId, Optional.of(name));
-    }
-
-    private final String blobId;
-    private final Optional<String> name;
-
-    private DownloadPath(String blobId, Optional<String> name) {
-        this.blobId = blobId;
-        this.name = name;
-    }
-
-    public String getBlobId() {
-        return blobId;
-    }
-
-    public Optional<String> getName() {
-        return name;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java
deleted file mode 100644
index f7f89b3..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import java.util.Date;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import jakarta.mail.Flags.Flag;
-
-import org.apache.james.jmap.draft.model.Filter;
-import org.apache.james.jmap.draft.model.FilterCondition;
-import org.apache.james.jmap.draft.model.FilterOperator;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.AddressType;
-import org.apache.james.mailbox.model.SearchQuery.Criterion;
-import org.apache.james.mailbox.model.SearchQuery.DateResolution;
-
-import com.google.common.collect.ImmutableList;
-
-public class FilterToCriteria {
-
-    public Stream<Criterion> convert(Filter filter) {
-        if (filter instanceof FilterCondition) {
-            return convertCondition((FilterCondition) filter);
-        }
-        if (filter instanceof FilterOperator) {
-            return Stream.of(convertOperator((FilterOperator) filter));
-        }
-        throw new RuntimeException("Unknown filter: " + filter.getClass());
-    }
-
-    private Stream<Criterion> convertCondition(FilterCondition filter) {
-        ImmutableList.Builder<Criterion> builder = ImmutableList.builder();
-        filter.getText().ifPresent(text -> builder.add(
-                SearchQuery.or(ImmutableList.of(
-                        SearchQuery.address(AddressType.From, text),
-                        SearchQuery.address(AddressType.To, text),
-                        SearchQuery.address(AddressType.Cc, text),
-                        SearchQuery.address(AddressType.Bcc, text),
-                        SearchQuery.subject(text),
-                        SearchQuery.attachmentContains(text),
-                        SearchQuery.bodyContains(text),
-                        SearchQuery.attachmentFileName(text)))
-                ));
-        filter.getFrom().ifPresent(from -> builder.add(SearchQuery.address(AddressType.From, from)));
-        filter.getTo().ifPresent(to -> builder.add(SearchQuery.address(AddressType.To, to)));
-        filter.getCc().ifPresent(cc -> builder.add(SearchQuery.address(AddressType.Cc, cc)));
-        filter.getBcc().ifPresent(bcc -> builder.add(SearchQuery.address(AddressType.Bcc, bcc)));
-        filter.getSubject().ifPresent(subject -> builder.add(SearchQuery.subject(subject)));
-        filter.getAttachments().ifPresent(attachments ->  builder.add(SearchQuery.attachmentContains(attachments)));
-        filter.getBody().ifPresent(body ->  builder.add(SearchQuery.bodyContains(body)));
-        filter.getAfter().ifPresent(after -> builder.add(SearchQuery.sentDateAfter(Date.from(after.toInstant()), DateResolution.Second)));
-        filter.getBefore().ifPresent(before -> builder.add(SearchQuery.sentDateBefore(Date.from(before.toInstant()), DateResolution.Second)));
-        filter.getHeader().ifPresent(header -> builder.add(SearchQuery.headerContains(header.getName(), header.getValue().orElse(null))));
-        filter.getIsAnswered().ifPresent(isAnswered -> builder.add(SearchQuery.flag(Flag.ANSWERED, isAnswered)));
-        filter.getIsDraft().ifPresent(isDraft -> builder.add(SearchQuery.flag(Flag.DRAFT, isDraft)));
-        filter.getIsFlagged().ifPresent(isFlagged -> builder.add(SearchQuery.flag(Flag.FLAGGED, isFlagged)));
-        filter.getIsUnread().ifPresent(isUnread -> builder.add(SearchQuery.flag(Flag.SEEN, !isUnread)));
-        filter.getIsForwarded().ifPresent(isForwarded -> builder.add(SearchQuery.flagSet(Keyword.FORWARDED.getFlagName(), isForwarded)));
-        filter.getMaxSize().ifPresent(maxSize -> builder.add(SearchQuery.sizeLessThan(maxSize.asLong())));
-        filter.getMinSize().ifPresent(minSize -> builder.add(SearchQuery.sizeGreaterThan(minSize.asLong())));
-        filter.getHasAttachment().ifPresent(hasAttachment -> builder.add(SearchQuery.hasAttachment(hasAttachment)));
-        filter.getHasKeyword().ifPresent(hasKeyword -> keywordQuery(hasKeyword, true).ifPresent(builder::add));
-        filter.getNotKeyword().ifPresent(notKeyword -> keywordQuery(notKeyword, false).ifPresent(builder::add));
-        filter.getAttachmentFileName().ifPresent(attachmentFileName -> builder.add(SearchQuery.attachmentFileName(attachmentFileName)));
-
-        return builder.build().stream();
-    }
-
-    private Optional<Criterion> keywordQuery(String stringKeyword, boolean isSet) {
-        Keyword keyword = Keyword.of(stringKeyword);
-        if (keyword.isExposedImapKeyword()) {
-            return Optional.of(getFlagCriterion(keyword, isSet));
-        }
-
-        return Optional.empty();
-    }
-
-    private Criterion getFlagCriterion(Keyword keyword, boolean isSet) {
-        return keyword.asSystemFlag()
-            .map(flag -> SearchQuery.flagSet(flag, isSet))
-            .orElseGet(() -> SearchQuery.flagSet(keyword.getFlagName(), isSet));
-    }
-
-    private Criterion convertOperator(FilterOperator filter) {
-        switch (filter.getOperator()) {
-        case AND:
-            return SearchQuery.and(convertCriterias(filter));
-   
-        case OR:
-            return SearchQuery.or(convertCriterias(filter));
-   
-        case NOT:
-            return SearchQuery.not(convertCriterias(filter));
-        }
-        throw new RuntimeException("Unknown operator");
-    }
-
-    private ImmutableList<Criterion> convertCriterias(FilterOperator filter) {
-        return filter.getConditions().stream()
-            .flatMap(this::convert)
-            .collect(ImmutableList.toImmutableList());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/MailboxUtils.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/MailboxUtils.java
deleted file mode 100644
index 823505e..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/MailboxUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************
-O * 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.draft.utils;
-
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.model.MailboxId;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-
-public class MailboxUtils {
-    private final MailboxManager mailboxManager;
-
-    @Inject
-    @VisibleForTesting
-    public MailboxUtils(MailboxManager mailboxManager) {
-        this.mailboxManager = mailboxManager;
-    }
-
-    public boolean hasChildren(MailboxId mailboxId, MailboxSession mailboxSession) throws MailboxException {
-        return getMailboxFromId(mailboxId, mailboxSession)
-                .map(Throwing.function(MessageManager::getMailboxPath).sneakyThrow())
-                .map(Throwing.function(path -> mailboxManager.hasChildren(path, mailboxSession)))
-                .orElse(false);
-    }
-
-    private Optional<MessageManager> getMailboxFromId(MailboxId mailboxId, MailboxSession mailboxSession) throws MailboxException {
-        try {
-            return Optional.of(mailboxManager.getMailbox(mailboxId, mailboxSession));
-        } catch (MailboxNotFoundException e) {
-            return Optional.empty();
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortConverter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortConverter.java
deleted file mode 100644
index 8befa07..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortConverter.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************
- O * 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.draft.utils;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.Sort;
-import org.apache.james.mailbox.model.SearchQuery.Sort.Order;
-import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SortConverter {
-
-    private static final String SEPARATOR = " ";
-    private static final String DESC_ORDERING = "desc";
-    private static final String ASC_ORDERING = "asc";
-
-    private static final Map<String, SortClause> SORT_CLAUSE_MAP =
-        ImmutableMap.<String, SortClause>builder()
-            .put("date", SortClause.SentDate)
-            .put("id", SortClause.Id)
-            .put("subject", SortClause.BaseSubject)
-            .put("from", SortClause.MailboxFrom)
-            .put("to", SortClause.MailboxTo)
-            .put("size", SortClause.Size)
-            .build();
-
-
-    public static List<Sort> convertToSorts(List<String> jmapSorts) {
-        Preconditions.checkNotNull(jmapSorts);
-        return jmapSorts.stream()
-            .map(SortConverter::toSort)
-            .collect(ImmutableList.toImmutableList());
-    }
-
-    private static Sort toSort(String jmapSort) {
-        Preconditions.checkNotNull(jmapSort);
-        List<String> splitToList = Splitter.on(SEPARATOR).splitToList(jmapSort);
-        checkField(splitToList);
-        return new SearchQuery.Sort(getSortClause(splitToList.get(0)),
-            isReverse(splitToList));
-    }
-
-    private static SortClause getSortClause(String field) {
-        if (! SORT_CLAUSE_MAP.containsKey(field)) {
-            throw new IllegalArgumentException("Unknown sorting field: " + field + " should be one of " + SORT_CLAUSE_MAP.keySet());
-        }
-        return SORT_CLAUSE_MAP.get(field);
-    }
-
-    private static Order isReverse(List<String> splitList) {
-        if (splitList.size() == 1) {
-            return Order.REVERSE;
-        }
-        String order = splitList.get(1);
-        switch (order) {
-            case DESC_ORDERING:
-                return Order.REVERSE;
-            case ASC_ORDERING:
-                return Order.NATURAL;
-        }
-        throw new IllegalArgumentException("Unknown sorting order: " + order + " should be one of [asc, desc]");
-    }
-
-    private static void checkField(List<String> splitToList) {
-        Preconditions.checkArgument(splitToList.size() > 0 && splitToList.size() <= 2, "Bad sort field definition. Must contains a field and an optional order separated by a space");
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollections.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollections.java
deleted file mode 100644
index 1ef0996..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollections.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.apache.james.jmap.draft.utils.DependencyGraph.CycleDetectedException;
-
-import com.google.common.collect.Lists;
-
-public class SortingHierarchicalCollections<T, IdT> {
-
-    private final Function<T, IdT> index;
-    private final Function<T, Optional<IdT>> parentId;
-
-    public SortingHierarchicalCollections(Function<T, IdT> index,
-                                  Function<T, Optional<IdT>> parentId) {
-        this.index = index;
-        this.parentId = parentId;
-    }
-
-    public List<T> sortFromRootToLeaf(Collection<T> elements) throws CycleDetectedException {
-
-        Map<IdT, T> mapOfElementsById = indexElementsById(elements);
-
-        DependencyGraph<T> graph = new DependencyGraph<>(m ->
-                parentId.apply(m).map(mapOfElementsById::get));
-
-        elements.forEach(graph::registerItem);
-
-        return graph.getBuildChain().collect(Collectors.toList());
-    }
-
-    private Map<IdT, T> indexElementsById(Collection<T> elements) {
-        return elements.stream()
-                .collect(Collectors.toMap(index, Function.identity()));
-    }
-
-    public List<T> sortFromLeafToRoot(Collection<T> elements) throws CycleDetectedException {
-        return Lists.reverse(sortFromRootToLeaf(elements));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/DefaultQuotaLoader.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/DefaultQuotaLoader.java
deleted file mode 100644
index 603ce08..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/DefaultQuotaLoader.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.utils.quotas;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.draft.model.mailbox.Quotas;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-
-import reactor.core.publisher.Mono;
-
-public class DefaultQuotaLoader extends QuotaLoader {
-
-    private final QuotaRootResolver quotaRootResolver;
-    private final QuotaManager quotaManager;
-
-    @Inject
-    public DefaultQuotaLoader(QuotaRootResolver quotaRootResolver, QuotaManager quotaManager) {
-        this.quotaRootResolver = quotaRootResolver;
-        this.quotaManager = quotaManager;
-    }
-
-    public Mono<Quotas> getQuotas(MailboxPath mailboxPath) {
-        return Mono.from(quotaRootResolver.getQuotaRootReactive(mailboxPath))
-            .flatMap(quotaRoot -> Mono.from(quotaManager.getQuotasReactive(quotaRoot))
-                .map(quotas -> {
-                    Quotas.QuotaId quotaId = Quotas.QuotaId.fromQuotaRoot(quotaRoot);
-
-                    return Quotas.from(
-                        quotaId,
-                        Quotas.Quota.from(
-                            quotaToValue(quotas.getStorageQuota()),
-                            quotaToValue(quotas.getMessageQuota())));
-                }));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoader.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoader.java
deleted file mode 100644
index b8b8e2d..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoader.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.utils.quotas;
-
-import java.util.Optional;
-
-import org.apache.james.core.quota.QuotaLimitValue;
-import org.apache.james.core.quota.QuotaUsageValue;
-import org.apache.james.jmap.draft.model.mailbox.Quotas;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.Quota;
-
-import reactor.core.publisher.Mono;
-
-public abstract class QuotaLoader {
-
-    public abstract Mono<Quotas> getQuotas(MailboxPath mailboxPath);
-
-    protected <T extends QuotaLimitValue<T>, U extends QuotaUsageValue<U, T>> Quotas.Value<T, U> quotaToValue(Quota<T, U> quota) {
-        return new Quotas.Value<>(
-            quotaValueUsageToNumber(quota.getUsed()),
-            quotaLimitValueToOptionalNumber(quota.getLimit()));
-    }
-
-    protected Number quotaValueToNumber(QuotaLimitValue<?> value) {
-        return Number.BOUND_SANITIZING_FACTORY.from(value.asLong());
-    }
-
-    protected Number quotaValueUsageToNumber(QuotaUsageValue<?, ?> value) {
-        return Number.BOUND_SANITIZING_FACTORY.from(value.asLong());
-    }
-
-    protected Optional<Number> quotaLimitValueToOptionalNumber(QuotaLimitValue<?> value) {
-        if (value.isUnlimited()) {
-            return Optional.empty();
-        }
-        return Optional.of(quotaValueToNumber(value));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoaderWithDefaultPreloaded.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoaderWithDefaultPreloaded.java
deleted file mode 100644
index 876dfbd..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/quotas/QuotaLoaderWithDefaultPreloaded.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.utils.quotas;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.model.mailbox.Quotas;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-
-import reactor.core.publisher.Mono;
-
-public class QuotaLoaderWithDefaultPreloaded extends QuotaLoader {
-
-    public static Mono<QuotaLoaderWithDefaultPreloaded> preLoad(QuotaRootResolver quotaRootResolver,
-                                            QuotaManager quotaManager,
-                                            MailboxSession session) {
-        DefaultQuotaLoader defaultQuotaLoader = new DefaultQuotaLoader(quotaRootResolver, quotaManager);
-
-        return defaultQuotaLoader.getQuotas(MailboxPath.inbox(session))
-            .map(Optional::of)
-            .switchIfEmpty(Mono.just(Optional.empty()))
-            .map(quotas -> new QuotaLoaderWithDefaultPreloaded(quotaRootResolver, defaultQuotaLoader, quotas));
-    }
-
-    private final QuotaRootResolver quotaRootResolver;
-    private final DefaultQuotaLoader defaultQuotaLoader;
-    private final Optional<Quotas> preloadedUserDefaultQuotas;
-
-    private QuotaLoaderWithDefaultPreloaded(QuotaRootResolver quotaRootResolver, DefaultQuotaLoader defaultQuotaLoader, Optional<Quotas> preloadedUserDefaultQuotas) {
-        this.quotaRootResolver = quotaRootResolver;
-        this.defaultQuotaLoader = defaultQuotaLoader;
-        this.preloadedUserDefaultQuotas = preloadedUserDefaultQuotas;
-    }
-
-    public Mono<Quotas> getQuotas(MailboxPath mailboxPath) {
-        return Mono.from(quotaRootResolver.getQuotaRootReactive(mailboxPath))
-            .flatMap(quotaRoot -> {
-                Quotas.QuotaId quotaId = Quotas.QuotaId.fromQuotaRoot(quotaRoot);
-                if (containsQuotaId(preloadedUserDefaultQuotas, quotaId)) {
-                    return Mono.just(preloadedUserDefaultQuotas.get());
-                }
-                return defaultQuotaLoader.getQuotas(mailboxPath);
-            });
-    }
-
-    private boolean containsQuotaId(Optional<Quotas> preloadedUserDefaultQuotas, Quotas.QuotaId quotaId) {
-        return preloadedUserDefaultQuotas
-            .map(Quotas::getQuotas)
-            .map(quotaIdQuotaMap -> quotaIdQuotaMap.containsKey(quotaId))
-            .orElse(false);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategy.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategy.java
deleted file mode 100644
index 3fdcefe..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategy.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
-import org.apache.james.jmap.api.access.exceptions.NotAnAccessTokenException;
-import org.apache.james.jmap.draft.api.AccessTokenManager;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMap;
-
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class AccessTokenAuthenticationStrategy implements AuthenticationStrategy {
-    private final AccessTokenManager accessTokenManager;
-    private final MailboxManager mailboxManager;
-
-    @Inject
-    @VisibleForTesting
-    AccessTokenAuthenticationStrategy(AccessTokenManager accessTokenManager, MailboxManager mailboxManager) {
-        this.accessTokenManager = accessTokenManager;
-        this.mailboxManager = mailboxManager;
-    }
-
-    @Override
-    public Mono<MailboxSession> createMailboxSession(HttpServerRequest httpRequest) {
-        return Mono.fromCallable(() -> authHeaders(httpRequest))
-            .filter(tokenString -> !tokenString.startsWith("Bearer"))
-            .map(AccessToken::fromString)
-            .flatMap(item -> Mono.from(accessTokenManager.getUsernameFromToken(item)))
-            .map(Throwing.function(user -> mailboxManager.authenticate(user).withoutDelegation()))
-            .onErrorResume(InvalidAccessToken.class, error -> Mono.error(new UnauthorizedException("Invalid access token", error)))
-            .onErrorResume(NotAnAccessTokenException.class, error -> Mono.error(new UnauthorizedException("Not an access token", error)));
-    }
-
-    @Override
-    public AuthenticationChallenge correspondingChallenge() {
-        return AuthenticationChallenge.of(
-            AuthenticationScheme.of("Bearer"),
-            ImmutableMap.of("realm", "JMAP Draft access token"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java
deleted file mode 100644
index 72ee241..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.http;
-
-import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT;
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
-import static io.netty.handler.codec.http.HttpResponseStatus.CREATED;
-import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
-import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
-import static io.netty.handler.codec.http.HttpResponseStatus.OK;
-import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED;
-import static org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE;
-import static org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE_UTF8;
-import static org.apache.james.jmap.JMAPUrls.AUTHENTICATION;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAuthContext;
-import static org.apache.james.jmap.http.LoggingHelper.jmapContext;
-import static org.apache.james.util.ReactorUtils.log;
-import static org.apache.james.util.ReactorUtils.logOnError;
-
-import java.io.IOException;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.Endpoint;
-import org.apache.james.jmap.JMAPRoute;
-import org.apache.james.jmap.JMAPRoutes;
-import org.apache.james.jmap.JMAPUrls;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.draft.api.AccessTokenManager;
-import org.apache.james.jmap.draft.api.SimpleTokenFactory;
-import org.apache.james.jmap.draft.api.SimpleTokenManager;
-import org.apache.james.jmap.draft.exceptions.BadRequestException;
-import org.apache.james.jmap.draft.exceptions.InternalErrorException;
-import org.apache.james.jmap.draft.json.MultipleObjectMapperBuilder;
-import org.apache.james.jmap.draft.model.AccessTokenRequest;
-import org.apache.james.jmap.draft.model.AccessTokenResponse;
-import org.apache.james.jmap.draft.model.ContinuationTokenRequest;
-import org.apache.james.jmap.draft.model.ContinuationTokenResponse;
-import org.apache.james.jmap.draft.model.EndPointsResponse;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.user.api.UsersRepository;
-import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.james.util.ReactorUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import io.netty.handler.codec.http.HttpMethod;
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-import reactor.netty.http.server.HttpServerRequest;
-import reactor.netty.http.server.HttpServerResponse;
-
-public class AuthenticationRoutes implements JMAPRoutes {
-    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationRoutes.class);
-
-    private final ObjectMapper mapper;
-    private final UsersRepository usersRepository;
-    private final SimpleTokenManager simpleTokenManager;
-    private final AccessTokenManager accessTokenManager;
-    private final SimpleTokenFactory simpleTokenFactory;
-    private final MetricFactory metricFactory;
-    private final Authenticator authenticator;
-
-    @Inject
-    public AuthenticationRoutes(UsersRepository usersRepository, SimpleTokenManager simpleTokenManager, AccessTokenManager accessTokenManager, SimpleTokenFactory simpleTokenFactory, MetricFactory metricFactory, @Named(InjectionKeys.DRAFT) Authenticator authenticator) {
-        this.mapper = new MultipleObjectMapperBuilder()
-            .registerClass(ContinuationTokenRequest.UNIQUE_JSON_PATH, ContinuationTokenRequest.class)
-            .registerClass(AccessTokenRequest.UNIQUE_JSON_PATH, AccessTokenRequest.class)
-            .build();
-        this.usersRepository = usersRepository;
-        this.simpleTokenManager = simpleTokenManager;
-        this.accessTokenManager = accessTokenManager;
-        this.simpleTokenFactory = simpleTokenFactory;
-        this.metricFactory = metricFactory;
-        this.authenticator = authenticator;
-    }
-
-    @Override
-    public Stream<JMAPRoute> routes() {
-        return Stream.of(
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.POST, AUTHENTICATION))
-                .action(this::post)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.GET, AUTHENTICATION))
-                .action(this::returnEndPointsResponse)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.DELETE, AUTHENTICATION))
-                .action(this::delete)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.OPTIONS, AUTHENTICATION))
-                .action(CORS_CONTROL)
-                .noCorsHeaders()
-        );
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response) {
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-authentication-post",
-            Mono.just(request)
-                .map(this::assertJsonContentType)
-                .map(this::assertAcceptJsonOnly)
-                .flatMap(this::deserialize)
-                .flatMap(objectRequest -> {
-                    if (objectRequest instanceof ContinuationTokenRequest) {
-                        return handleContinuationTokenRequest((ContinuationTokenRequest) objectRequest, response);
-                    } else if (objectRequest instanceof AccessTokenRequest) {
-                        return handleAccessTokenRequest((AccessTokenRequest) objectRequest, response);
-                    } else {
-                        throw new RuntimeException(objectRequest.getClass() + " " + objectRequest);
-                    }
-                })))
-            .onErrorResume(BadRequestException.class, e -> handleBadRequest(response, LOGGER, e))
-            .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-            .onErrorResume(e -> handleInternalError(response, LOGGER, e))
-            .contextWrite(jmapContext(request))
-            .contextWrite(jmapAction("auth-post"))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private Mono<Void> returnEndPointsResponse(HttpServerRequest req, HttpServerResponse resp) {
-            return authenticator.authenticate(req)
-                .flatMap(session -> returnEndPointsResponse(resp)
-                    .contextWrite(jmapAuthContext(session)))
-                .onErrorResume(IllegalArgumentException.class, e -> handleBadRequest(resp, LOGGER, e))
-                .onErrorResume(BadRequestException.class, e -> handleBadRequest(resp, LOGGER, e))
-                .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-                .onErrorResume(InternalErrorException.class, e -> handleInternalError(resp, LOGGER, e))
-                .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(resp, LOGGER, e))
-                .contextWrite(jmapContext(req))
-                .contextWrite(jmapAction("returnEndPoints"))
-                .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private Mono<Void> returnEndPointsResponse(HttpServerResponse resp) {
-        try {
-            byte[] bytes = mapper.writeValueAsBytes(EndPointsResponse
-                .builder()
-                .api(JMAPUrls.JMAP)
-                .eventSource(JMAPUrls.NOT_IMPLEMENTED)
-                .upload(JMAPUrls.UPLOAD)
-                .download(JMAPUrls.DOWNLOAD)
-                .build());
-
-            return resp.status(OK)
-                .header(CONTENT_TYPE, JSON_CONTENT_TYPE_UTF8)
-                .header(CONTENT_LENGTH, Integer.toString(bytes.length))
-                .sendByteArray(Mono.just(bytes))
-                .then();
-        } catch (JsonProcessingException e) {
-            throw new InternalErrorException("Error serializing endpoint response", e);
-        }
-    }
-
-    private Mono<Void> delete(HttpServerRequest req, HttpServerResponse resp) {
-        String authorizationHeader = req.requestHeaders().get("Authorization");
-
-        return authenticator.authenticate(req)
-            .flatMap(session -> Mono.from(accessTokenManager.revoke(AccessToken.fromString(authorizationHeader)))
-                    .then(resp.status(NO_CONTENT).send().then())
-                .contextWrite(jmapAuthContext(session)))
-            .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(resp, LOGGER, e))
-            .contextWrite(jmapContext(req))
-            .contextWrite(jmapAction("auth-delete"))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private HttpServerRequest assertJsonContentType(HttpServerRequest req) {
-        if (!Objects.equals(req.requestHeaders().get(CONTENT_TYPE), JSON_CONTENT_TYPE_UTF8)) {
-            throw new BadRequestException("Request ContentType header must be set to: " + JSON_CONTENT_TYPE_UTF8);
-        }
-        return req;
-    }
-
-    private HttpServerRequest assertAcceptJsonOnly(HttpServerRequest req) {
-        String accept = req.requestHeaders().get(ACCEPT);
-        if (accept == null || !accept.contains(JSON_CONTENT_TYPE)) {
-            throw new BadRequestException("Request Accept header must be set to JSON content type");
-        }
-        return req;
-    }
-
-    private Mono<Object> deserialize(HttpServerRequest req) {
-        return req.receive().aggregate().asInputStream()
-            .map(inputStream -> {
-                try {
-                    return mapper.readValue(inputStream, Object.class);
-                } catch (IOException e) {
-                    throw new BadRequestException("Request can't be deserialized", e);
-                }
-            })
-            .switchIfEmpty(Mono.error(() -> new BadRequestException("Empty body")));
-    }
-
-    private Mono<Void> handleContinuationTokenRequest(ContinuationTokenRequest request, HttpServerResponse resp) {
-        try {
-            Mono<byte[]> tokenResponseMono = Mono.fromCallable(() -> ContinuationTokenResponse
-                .builder()
-                .continuationToken(simpleTokenFactory.generateContinuationToken(request.getUsername()))
-                .methods(ContinuationTokenResponse.AuthenticationMethod.PASSWORD)
-                .build())
-                .map(token -> {
-                    try {
-                        return mapper.writeValueAsBytes(token);
-                    } catch (JsonProcessingException e) {
-                        throw new InternalErrorException("error serialising JMAP API response json");
-                    }
-                })
-                .subscribeOn(Schedulers.parallel());
-
-            return tokenResponseMono
-                .flatMap(bytes -> resp.header(CONTENT_TYPE, JSON_CONTENT_TYPE_UTF8)
-                    .header(CONTENT_LENGTH, Integer.toString(bytes.length))
-                    .sendByteArray(Mono.just(bytes))
-                    .then());
-        } catch (Exception e) {
-            throw new InternalErrorException("Error while responding to continuation token", e);
-        }
-    }
-
-    private Mono<Void> handleAccessTokenRequest(AccessTokenRequest request, HttpServerResponse resp) {
-        SimpleTokenManager.TokenStatus validity = simpleTokenManager.getValidity(request.getToken());
-        switch (validity) {
-            case EXPIRED:
-                return returnForbiddenAuthentication(resp);
-            case INVALID:
-                return returnUnauthorizedResponse(resp)
-                    .doOnEach(log(() -> LOGGER.warn("Use of an invalid ContinuationToken : {}", request.getToken().serialize())));
-            case OK:
-                return manageAuthenticationResponse(request, resp);
-            default:
-                throw new InternalErrorException(String.format("Validity %s is not implemented", validity));
-        }
-    }
-
-    private Mono<Void> manageAuthenticationResponse(AccessTokenRequest request, HttpServerResponse resp) {
-        Username username = Username.of(request.getToken().getUsername());
-
-        return authenticate(request, username)
-            .flatMap(loggedInUser -> loggedInUser.map(value -> returnAccessTokenResponse(resp, value))
-                .orElseGet(() -> returnUnauthorizedResponse(resp)
-                    .doOnEach(log(() -> LOGGER.info("Authentication failure for {}", username)))));
-    }
-
-    private Mono<Optional<Username>> authenticate(AccessTokenRequest request, Username username) {
-        return Mono.fromCallable(() -> {
-            try {
-                return usersRepository.test(username, request.getPassword());
-            } catch (UsersRepositoryException e) {
-                LOGGER.error("Error while trying to validate authentication for user '{}'", username, e);
-                return Optional.<Username>empty();
-            }
-        }).subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private Mono<Void> returnAccessTokenResponse(HttpServerResponse resp, Username username) {
-        return Mono.from(accessTokenManager.grantAccessToken(username))
-            .map(accessToken -> AccessTokenResponse.builder()
-                .accessToken(accessToken)
-                .api(JMAPUrls.JMAP)
-                .eventSource(JMAPUrls.NOT_IMPLEMENTED)
-                .upload(JMAPUrls.UPLOAD)
-                .download(JMAPUrls.DOWNLOAD)
-                .build())
-            .flatMap(accessTokenResponse -> {
-                try {
-                    byte[] bytes = mapper.writeValueAsBytes(accessTokenResponse);
-                    return resp.status(CREATED)
-                        .header(CONTENT_TYPE, JSON_CONTENT_TYPE_UTF8)
-                        .header(CONTENT_LENGTH, Integer.toString(bytes.length))
-                        .sendByteArray(Mono.just(bytes))
-                        .then();
-                } catch (JsonProcessingException e) {
-                    throw new InternalErrorException("Could not serialize access token response", e);
-                }
-            });
-    }
-
-    private Mono<Void> returnUnauthorizedResponse(HttpServerResponse resp) {
-        return resp.status(UNAUTHORIZED).send().then();
-    }
-
-    private Mono<Void> returnForbiddenAuthentication(HttpServerResponse resp) {
-        return resp.status(FORBIDDEN).send().then();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java
deleted file mode 100644
index 316804b..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.apache.james.util.FunctionalUtils.negate;
-import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY;
-
-import java.util.function.Function;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.mailbox.DefaultMailboxes;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxExistsException;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.metrics.api.MetricFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class DefaultMailboxesProvisioner {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMailboxesProvisioner.class);
-    private final MailboxManager mailboxManager;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    public DefaultMailboxesProvisioner(MailboxManager mailboxManager,
-                                MetricFactory metricFactory) {
-        this.mailboxManager = mailboxManager;
-        this.metricFactory = metricFactory;
-    }
-
-    public Mono<Void> createMailboxesIfNeeded(MailboxSession session) {
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-mailboxes-provisioning",
-            createDefaultMailboxes(session)));
-    }
-
-    private Mono<Void> createDefaultMailboxes(MailboxSession session) {
-        return Flux.fromIterable(DefaultMailboxes.DEFAULT_MAILBOXES)
-            .map(toMailboxPath(session))
-            .filterWhen(mailboxPath -> mailboxDoesntExist(mailboxPath, session), DEFAULT_CONCURRENCY)
-            .concatMap(mailboxPath -> createMailbox(mailboxPath, session))
-            .then();
-    }
-
-    private Mono<Boolean> mailboxDoesntExist(MailboxPath mailboxPath, MailboxSession session) {
-        return Mono.from(mailboxManager.mailboxExists(mailboxPath, session))
-            .map(negate());
-    }
-
-    private Function<String, MailboxPath> toMailboxPath(MailboxSession session) {
-        return mailbox -> MailboxPath.forUser(session.getUser(), mailbox);
-    }
-    
-    private Mono<Void> createMailbox(MailboxPath mailboxPath, MailboxSession session) {
-        return Mono.from(mailboxManager.createMailboxReactive(mailboxPath, MailboxManager.CreateOption.CREATE_SUBSCRIPTION, session))
-            .onErrorResume(MailboxExistsException.class, e -> {
-                LOGGER.info("Mailbox {} have been created concurrently", mailboxPath);
-                return Mono.empty();
-            })
-            .then();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
deleted file mode 100644
index 142e543..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
-import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
-import static io.netty.handler.codec.http.HttpResponseStatus.OK;
-import static org.apache.james.jmap.HttpConstants.TEXT_PLAIN_CONTENT_TYPE;
-import static org.apache.james.jmap.JMAPUrls.DOWNLOAD;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAuthContext;
-import static org.apache.james.jmap.http.LoggingHelper.jmapContext;
-import static org.apache.james.util.ReactorUtils.logOnError;
-
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
-import org.apache.james.jmap.Endpoint;
-import org.apache.james.jmap.JMAPRoute;
-import org.apache.james.jmap.JMAPRoutes;
-import org.apache.james.jmap.draft.api.SimpleTokenFactory;
-import org.apache.james.jmap.draft.exceptions.BadRequestException;
-import org.apache.james.jmap.draft.exceptions.InternalErrorException;
-import org.apache.james.jmap.draft.model.AttachmentAccessToken;
-import org.apache.james.jmap.draft.utils.DownloadPath;
-import org.apache.james.jmap.exceptions.BlobNotFoundException;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.ContentType;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.mime4j.codec.EncoderUtil;
-import org.apache.james.mime4j.codec.EncoderUtil.Usage;
-import org.apache.james.util.ReactorUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.CharMatcher;
-import com.google.common.collect.ImmutableList;
-
-import io.netty.buffer.Unpooled;
-import io.netty.handler.codec.http.HttpHeaderValidationUtil;
-import io.netty.handler.codec.http.HttpMethod;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-import reactor.netty.http.server.HttpServerRequest;
-import reactor.netty.http.server.HttpServerResponse;
-
-public class DownloadRoutes implements JMAPRoutes {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DownloadRoutes.class);
-    static final String BLOB_ID_PATH_PARAM = "blobId";
-    private static final String NAME_PATH_PARAM = "name";
-    private static final String DOWNLOAD_FROM_ID = String.format("%s/{%s}", DOWNLOAD, BLOB_ID_PATH_PARAM);
-    private static final String DOWNLOAD_FROM_ID_AND_NAME = String.format("%s/{%s}/{%s}", DOWNLOAD, BLOB_ID_PATH_PARAM, NAME_PATH_PARAM);
-    private static final int BUFFER_SIZE = 16 * 1024;
-
-    private final BlobManager blobManager;
-    private final SimpleTokenFactory simpleTokenFactory;
-    private final MetricFactory metricFactory;
-    private final Authenticator authenticator;
-
-    @Inject
-    @VisibleForTesting
-    DownloadRoutes(BlobManager blobManager, SimpleTokenFactory simpleTokenFactory, MetricFactory metricFactory, @Named(InjectionKeys.DRAFT) Authenticator authenticator) {
-        this.blobManager = blobManager;
-        this.simpleTokenFactory = simpleTokenFactory;
-        this.metricFactory = metricFactory;
-        this.authenticator = authenticator;
-    }
-
-    @Override
-    public Stream<JMAPRoute> routes() {
-        return Stream.of(
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.POST, DOWNLOAD_FROM_ID))
-                .action(this::postFromId)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.GET, DOWNLOAD_FROM_ID))
-                .action(this::getFromId)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.POST, DOWNLOAD_FROM_ID_AND_NAME))
-                .action(this::postFromIdAndName)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.GET, DOWNLOAD_FROM_ID_AND_NAME))
-                .action(this::getFromIdAndName)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.OPTIONS, DOWNLOAD_FROM_ID))
-                .action(CORS_CONTROL)
-                .noCorsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.OPTIONS, DOWNLOAD_FROM_ID_AND_NAME))
-                .action(CORS_CONTROL)
-                .noCorsHeaders()
-        );
-    }
-
-    private Mono<Void> postFromId(HttpServerRequest request, HttpServerResponse response) {
-        String blobId = request.param(BLOB_ID_PATH_PARAM);
-        DownloadPath downloadPath = DownloadPath.ofBlobId(blobId);
-        return post(request, response, downloadPath);
-    }
-
-    private Mono<Void> postFromIdAndName(HttpServerRequest request, HttpServerResponse response) {
-        String blobId = request.param(BLOB_ID_PATH_PARAM);
-        String name = request.param(NAME_PATH_PARAM);
-        DownloadPath downloadPath = DownloadPath.of(blobId, name);
-        return post(request, response, downloadPath);
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, DownloadPath downloadPath) {
-        return authenticator.authenticate(request)
-            .flatMap(session -> Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-download-post",
-                    respondAttachmentAccessToken(session, downloadPath, response)))
-                .contextWrite(jmapAuthContext(session)))
-            .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
-            .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-            .onErrorResume(e -> handleInternalError(response, LOGGER, e))
-            .contextWrite(jmapContext(request))
-            .contextWrite(jmapAction("download-post"))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private Mono<Void> getFromId(HttpServerRequest request, HttpServerResponse response) {
-        String blobId = request.param(BLOB_ID_PATH_PARAM);
-        DownloadPath downloadPath = DownloadPath.ofBlobId(blobId);
-        return get(request, response, downloadPath);
-    }
-
-    private Mono<Void> getFromIdAndName(HttpServerRequest request, HttpServerResponse response) {
-        String blobId = request.param(BLOB_ID_PATH_PARAM);
-        try {
-            String name = URLDecoder.decode(request.param(NAME_PATH_PARAM), StandardCharsets.UTF_8.toString());
-            DownloadPath downloadPath = DownloadPath.of(blobId, name);
-            return get(request, response, downloadPath);
-        } catch (UnsupportedEncodingException e) {
-            throw new BadRequestException("Wrong url encoding", e);
-        }
-    }
-
-    private Mono<Void> get(HttpServerRequest request, HttpServerResponse response, DownloadPath downloadPath) {
-        return authenticator.authenticate(request)
-            .flatMap(session -> Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-download-get",
-                    download(session, downloadPath, response)))
-                .contextWrite(jmapAuthContext(session)))
-            .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
-            .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-            .onErrorResume(IllegalArgumentException.class, e -> handleBadRequest(response, LOGGER, e))
-            .onErrorResume(e -> handleInternalError(response, LOGGER, e))
-            .contextWrite(jmapContext(request))
-            .contextWrite(jmapAction("download-get"))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-    }
-
-    private Mono<Void> respondAttachmentAccessToken(MailboxSession mailboxSession, DownloadPath downloadPath, HttpServerResponse resp) {
-        String blobId = downloadPath.getBlobId();
-
-        return attachmentExists(mailboxSession, blobId)
-            .flatMap(exists -> {
-                if (exists) {
-                    AttachmentAccessToken attachmentAccessToken = simpleTokenFactory.generateAttachmentAccessToken(mailboxSession.getUser().asString(), blobId);
-                    byte[] bytes = attachmentAccessToken.serialize().getBytes(StandardCharsets.UTF_8);
-                    return resp.header(CONTENT_TYPE, TEXT_PLAIN_CONTENT_TYPE)
-                        .status(OK)
-                        .header(CONTENT_LENGTH, Integer.toString(bytes.length))
-                        .sendByteArray(Mono.just(bytes))
-                        .then();
-                } else {
-                    return resp.status(NOT_FOUND).send();
-                }
-            });
-    }
-
-    private Mono<Boolean> attachmentExists(MailboxSession mailboxSession, String blobId) {
-        return Flux.from(blobManager.retrieve(ImmutableList.of(BlobId.of(blobId)), mailboxSession))
-            .hasElements();
-    }
-
-    @VisibleForTesting
-    Mono<Void> download(MailboxSession mailboxSession, DownloadPath downloadPath, HttpServerResponse response) {
-        String blobId = downloadPath.getBlobId();
-
-        return Mono.from(blobManager.retrieve(ImmutableList.of(BlobId.of(blobId)), mailboxSession))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER)
-            .switchIfEmpty(Mono.error(() -> new BlobNotFoundException(BlobId.of(blobId))))
-            .flatMap(blob -> Mono.usingWhen(
-                blob.getStreamReactive(),
-                stream -> downloadBlob(downloadPath.getName(), response, blob.getSize(), blob.getContentType(), stream),
-                stream -> Mono.fromRunnable(Throwing.runnable(stream::close).sneakyThrow()))
-                .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER))
-            .onErrorResume(BlobNotFoundException.class, e -> {
-                LOGGER.info("Attachment '{}' not found", blobId, e);
-                return response.status(NOT_FOUND).send();
-            }).onErrorResume(MailboxException.class, e -> Mono.error(new InternalErrorException("Error while downloading", e)));
-    }
-
-    private Mono<Void> downloadBlob(Optional<String> optionalName, HttpServerResponse response, long blobSize, ContentType blobContentType, InputStream stream) {
-        return addContentDispositionHeader(optionalName, response)
-            .header("Content-Length", String.valueOf(blobSize))
-            .header(CONTENT_TYPE, sanitizeHeaderValue(blobContentType.asString()))
-            .status(OK)
-            .send(ReactorUtils.toChunks(stream, BUFFER_SIZE)
-                .map(Unpooled::wrappedBuffer)
-                .subscribeOn(Schedulers.boundedElastic()))
-            .then();
-    }
-
-    public String sanitizeHeaderValue(String s) {
-        if (HttpHeaderValidationUtil.validateValidHeaderValue(s) == -1) {
-            return s;
-        }
-        return "application/octet-stream";
-    }
-
-    private HttpServerResponse addContentDispositionHeader(Optional<String> optionalName, HttpServerResponse resp) {
-        return optionalName.map(name -> addContentDispositionHeaderRegardingEncoding(name, resp))
-            .orElse(resp);
-    }
-
-    private HttpServerResponse addContentDispositionHeaderRegardingEncoding(String name, HttpServerResponse resp) {
-        if (CharMatcher.ascii().matchesAllOf(name)) {
-            return resp.header("Content-Disposition", "attachment; filename=\"" + name + "\"");
-        } else {
-            return resp.header("Content-Disposition", "attachment; filename*=\"" + EncoderUtil.encodeEncodedWord(name, Usage.TEXT_TOKEN) + "\"");
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/InjectionKeys.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/InjectionKeys.java
deleted file mode 100644
index 5d2ae11..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/InjectionKeys.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.http;
-
-public interface InjectionKeys {
-    String DRAFT = "DRAFT";
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java
deleted file mode 100644
index b712877..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
-import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
-import static io.netty.handler.codec.http.HttpResponseStatus.OK;
-import static org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE;
-import static org.apache.james.jmap.JMAPUrls.JMAP;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAuthContext;
-import static org.apache.james.jmap.http.LoggingHelper.jmapContext;
-import static org.apache.james.util.ReactorUtils.logOnError;
-
-import java.io.IOException;
-import java.util.stream.Stream;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
-import org.apache.james.jmap.Endpoint;
-import org.apache.james.jmap.JMAPRoute;
-import org.apache.james.jmap.JMAPRoutes;
-import org.apache.james.jmap.draft.exceptions.BadRequestException;
-import org.apache.james.jmap.draft.exceptions.InternalErrorException;
-import org.apache.james.jmap.draft.methods.RequestHandler;
-import org.apache.james.jmap.draft.model.AuthenticatedRequest;
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonParser.Feature;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableList;
-
-import io.netty.handler.codec.http.HttpMethod;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerRequest;
-import reactor.netty.http.server.HttpServerResponse;
-
-public class JMAPApiRoutes implements JMAPRoutes {
-    public static final Logger LOGGER = LoggerFactory.getLogger(JMAPApiRoutes.class);
-
-    private final ObjectMapper objectMapper;
-    private final RequestHandler requestHandler;
-    private final MetricFactory metricFactory;
-    private final Authenticator authenticator;
-    private final UserProvisioner userProvisioner;
-
-    @Inject
-    public JMAPApiRoutes(RequestHandler requestHandler, MetricFactory metricFactory, @Named(InjectionKeys.DRAFT) Authenticator authenticator, UserProvisioner userProvisioner) {
-        this.requestHandler = requestHandler;
-        this.metricFactory = metricFactory;
-        this.authenticator = authenticator;
-        this.userProvisioner = userProvisioner;
-        this.objectMapper = new ObjectMapper();
-        objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
-    }
-
-    @Override
-    public Stream<JMAPRoute> routes() {
-        return Stream.of(
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.POST, JMAP))
-                .action(this::post)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(Endpoint.ofFixedPath(HttpMethod.OPTIONS, JMAP))
-                .action(CORS_CONTROL)
-                .noCorsHeaders()
-        );
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response) {
-        return authenticator.authenticate(request)
-            .flatMap(session -> userProvisioner.provisionUser(session)
-                .then(Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-request",
-                    post(request, response, session))))
-                .contextWrite(jmapAuthContext(session)))
-            .onErrorResume(BadRequestException.class, e -> handleBadRequest(response, LOGGER, e))
-            .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
-            .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-            .onErrorResume(e -> response.status(INTERNAL_SERVER_ERROR).send())
-            .contextWrite(jmapContext(request));
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, MailboxSession session) {
-        Flux<Object[]> responses =
-            requestAsJsonStream(request)
-                .map(InvocationRequest::deserialize)
-                .map(invocationRequest -> AuthenticatedRequest.decorate(invocationRequest, session))
-                .concatMap(requestHandler::handle)
-                .map(InvocationResponse::asProtocolSpecification);
-
-        return sendResponses(response, responses);
-    }
-
-    private Mono<Void> sendResponses(HttpServerResponse response, Flux<Object[]> responses) {
-        return responses.collectList()
-            .map(objects -> {
-                try {
-                    return objectMapper.writeValueAsBytes(objects);
-                } catch (JsonProcessingException e) {
-                    throw new InternalErrorException("error serialising JMAP API response json");
-                }
-            })
-            .flatMap(json -> response.status(OK)
-                .header(CONTENT_TYPE, JSON_CONTENT_TYPE)
-                .header(CONTENT_LENGTH, Integer.toString(json.length))
-                .sendByteArray(Mono.just(json))
-                .then());
-    }
-
-    private Flux<JsonNode[]> requestAsJsonStream(HttpServerRequest req) {
-        return req.receive().aggregate().asInputStream()
-            .map(inputStream -> {
-                try {
-                    return objectMapper.readValue(inputStream, JsonNode[][].class);
-                } catch (IOException e) {
-                    throw new BadRequestException("Error deserializing JSON", e);
-                }
-            })
-            .flatMapIterable(ImmutableList::copyOf);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/LoggingHelper.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/LoggingHelper.java
deleted file mode 100644
index 8e252ab..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/LoggingHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.http;
-
-import static org.apache.james.util.ReactorUtils.context;
-
-import java.util.Optional;
-
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.util.MDCBuilder;
-
-import reactor.netty.http.server.HttpServerRequest;
-import reactor.util.context.Context;
-
-public interface LoggingHelper {
-    static Context jmapAuthContext(MailboxSession session) {
-        return context("JMAP_AUTH",
-            MDCBuilder.ofValue(MDCBuilder.USER, session.getUser().asString()));
-    }
-
-    static Context jmapContext(HttpServerRequest req) {
-        return context("JMAP", MDCBuilder.create()
-            .addToContext(MDCBuilder.PROTOCOL, "JMAP")
-            .addToContext(MDCBuilder.IP, req.hostAddress().getHostString())
-            .addToContextIfPresent("real-ip", Optional.ofNullable(req.requestHeaders().getAsString("X-Real-IP"))));
-    }
-
-    static Context jmapAction(String action) {
-        return context("JMAP_ACTION",
-            MDCBuilder.ofValue(MDCBuilder.ACTION, action));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategy.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategy.java
deleted file mode 100644
index 33158d2..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategy.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.apache.james.jmap.http.DownloadRoutes.BLOB_ID_PATH_PARAM;
-
-import java.util.List;
-import java.util.Optional;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.api.SimpleTokenManager;
-import org.apache.james.jmap.draft.model.AttachmentAccessToken;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMap;
-
-import io.netty.handler.codec.http.QueryStringDecoder;
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class QueryParameterAccessTokenAuthenticationStrategy implements AuthenticationStrategy {
-    private static final String AUTHENTICATION_PARAMETER = "access_token";
-
-    private final SimpleTokenManager tokenManager;
-    private final MailboxManager mailboxManager;
-
-    @Inject
-    @VisibleForTesting
-    QueryParameterAccessTokenAuthenticationStrategy(SimpleTokenManager tokenManager, MailboxManager mailboxManager) {
-        this.tokenManager = tokenManager;
-        this.mailboxManager = mailboxManager;
-    }
-
-    @Override
-    public Mono<MailboxSession> createMailboxSession(HttpServerRequest httpRequest) {
-        return Mono.justOrEmpty(getAccessToken(httpRequest))
-            .filter(tokenManager::isValid)
-            .map(AttachmentAccessToken::getUsername)
-            .map(Username::of)
-            .map(Throwing.function(user -> mailboxManager.authenticate(user).withoutDelegation()));
-    }
-
-    @Override
-    public AuthenticationChallenge correspondingChallenge() {
-        return AuthenticationChallenge.of(
-            AuthenticationScheme.of("QueryParameterBearer"),
-            ImmutableMap.of("realm", "JMAP Draft access token over Query parameter"));
-    }
-
-    private Optional<AttachmentAccessToken> getAccessToken(HttpServerRequest httpRequest) {
-        try {
-
-            return Optional.ofNullable(httpRequest.param(BLOB_ID_PATH_PARAM))
-                .flatMap(blobId -> queryParam(httpRequest, AUTHENTICATION_PARAMETER)
-                    .map(serializedAttachmentAccessToken -> AttachmentAccessToken.from(serializedAttachmentAccessToken, blobId)));
-        } catch (IllegalArgumentException e) {
-            return Optional.empty();
-        }
-    }
-
-    private Optional<String> queryParam(HttpServerRequest httpRequest, String parameterName) {
-        return queryParam(parameterName, httpRequest.uri());
-    }
-
-    private Optional<String> queryParam(String parameterName, String uri) {
-        return Optional.ofNullable(new QueryStringDecoder(uri)
-                .parameters()
-                .get(parameterName))
-            .stream()
-            .flatMap(List::stream)
-            .findFirst();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
deleted file mode 100644
index 4c01347..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
-import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
-import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
-import static io.netty.handler.codec.http.HttpResponseStatus.CREATED;
-import static org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE_UTF8;
-import static org.apache.james.jmap.JMAPUrls.UPLOAD;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAction;
-import static org.apache.james.jmap.http.LoggingHelper.jmapAuthContext;
-import static org.apache.james.jmap.http.LoggingHelper.jmapContext;
-import static org.apache.james.util.ReactorUtils.logOnError;
-
-import java.io.EOFException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.stream.Stream;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
-import org.apache.james.jmap.Endpoint;
-import org.apache.james.jmap.JMAPRoute;
-import org.apache.james.jmap.JMAPRoutes;
-import org.apache.james.jmap.api.upload.UploadRepository;
-import org.apache.james.jmap.draft.exceptions.BadRequestException;
-import org.apache.james.jmap.draft.exceptions.InternalErrorException;
-import org.apache.james.jmap.draft.model.UploadResponse;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.jmap.methods.BlobManagerImpl;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.ContentType;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.util.ReactorUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Strings;
-
-import io.netty.handler.codec.http.HttpMethod;
-import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
-import reactor.netty.http.server.HttpServerRequest;
-import reactor.netty.http.server.HttpServerResponse;
-
-public class UploadRoutes implements JMAPRoutes {
-    private static final Logger LOGGER = LoggerFactory.getLogger(UploadRoutes.class);
-
-    static class CancelledUploadException extends RuntimeException {
-
-    }
-
-    private final MetricFactory metricFactory;
-    private final Authenticator authenticator;
-    private final UploadRepository uploadRepository;
-    private final ObjectMapper objectMapper;
-
-    @Inject
-    private UploadRoutes(MetricFactory metricFactory, @Named(InjectionKeys.DRAFT) Authenticator authenticator, UploadRepository uploadRepository, ObjectMapper objectMapper) {
-        this.metricFactory = metricFactory;
-        this.authenticator = authenticator;
-        this.uploadRepository = uploadRepository;
-        this.objectMapper = objectMapper;
-    }
-
-    @Override
-    public Stream<JMAPRoute> routes() {
-        return Stream.of(
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.POST, UPLOAD))
-                .action(this::post)
-                .corsHeaders(),
-            JMAPRoute.builder()
-                .endpoint(new Endpoint(HttpMethod.OPTIONS, UPLOAD))
-                .action(CORS_CONTROL)
-                .noCorsHeaders()
-        );
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response)  {
-        String contentType = request.requestHeaders().get(CONTENT_TYPE);
-        if (Strings.isNullOrEmpty(contentType)) {
-            return response.status(BAD_REQUEST).send();
-        } else {
-            return authenticator.authenticate(request)
-                .flatMap(session -> post(request, response, ContentType.of(contentType), session)
-                    .contextWrite(jmapAuthContext(session)))
-                .onErrorResume(CancelledUploadException.class, e -> handleCanceledUpload(response, e))
-                .onErrorResume(BadRequestException.class, e -> handleBadRequest(response, e))
-                .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
-                .doOnEach(logOnError(e -> LOGGER.error("Unexpected error", e)))
-                .onErrorResume(e -> handleInternalError(response, LOGGER, e))
-                .contextWrite(jmapContext(request))
-                .contextWrite(jmapAction("upload-get"))
-                .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
-        }
-    }
-
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, ContentType contentType, MailboxSession session) {
-        InputStream content = ReactorUtils.toInputStream(request.receive()
-            // Unwrapping to byte array needed to solve data races and buffer reordering when using .asByteBuffer()
-            .asByteArray()
-            .map(ByteBuffer::wrap)
-            .subscribeOn(Schedulers.boundedElastic()));
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-upload-post",
-            handle(contentType, content, session, response)));
-    }
-
-    private Mono<Void> handle(ContentType contentType, InputStream content, MailboxSession mailboxSession, HttpServerResponse response) {
-        return uploadContent(contentType, content, mailboxSession)
-            .flatMap(storedContent -> {
-                try {
-                    byte[] bytes = objectMapper.writeValueAsBytes(storedContent);
-                    return response.header(CONTENT_TYPE, JSON_CONTENT_TYPE_UTF8)
-                        .status(CREATED)
-                        .header(CONTENT_LENGTH, Integer.toString(bytes.length))
-                        .sendByteArray(Mono.just(bytes))
-                        .then();
-                } catch (JsonProcessingException e) {
-                    throw new InternalErrorException("Error serializing upload response", e);
-                }
-            });
-    }
-
-    private Mono<UploadResponse> uploadContent(ContentType contentType, InputStream inputStream, MailboxSession session) {
-        return Mono.from(uploadRepository.upload(inputStream, contentType, session.getUser()))
-            .map(upload -> UploadResponse.builder()
-                .blobId(BlobManagerImpl.UPLOAD_PREFIX + upload.uploadId().asString())
-                .type(upload.contentType().asString())
-                .size(upload.sizeAsLong())
-                .build())
-            .onErrorMap(e -> e.getCause() instanceof EOFException, any -> new CancelledUploadException())
-            .onErrorMap(e -> !(e instanceof CancelledUploadException), e -> new InternalErrorException("Error while uploading content", e));
-    }
-
-    private Mono<Void> handleCanceledUpload(HttpServerResponse response, CancelledUploadException e) {
-        LOGGER.info("An upload has been canceled before the end", e);
-        return response.send();
-    }
-
-    private Mono<Void> handleBadRequest(HttpServerResponse response, BadRequestException e) {
-        LOGGER.warn("Invalid authentication request received.", e);
-        return response.status(BAD_REQUEST).send();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java
deleted file mode 100644
index dd664ed..0000000
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import java.util.UUID;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.user.api.AlreadyExistInUsersRepositoryException;
-import org.apache.james.user.api.UsersRepository;
-import org.apache.james.util.FunctionalUtils;
-import org.apache.james.util.ReactorUtils;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.annotations.VisibleForTesting;
-
-import reactor.core.publisher.Mono;
-
-public class UserProvisioner {
-    private final JMAPConfiguration jmapConfiguration;
-    private final UsersRepository usersRepository;
-    private final MetricFactory metricFactory;
-
-    @Inject
-    @VisibleForTesting
-    UserProvisioner(JMAPConfiguration jmapConfiguration, UsersRepository usersRepository, MetricFactory metricFactory) {
-        this.jmapConfiguration = jmapConfiguration;
-        this.usersRepository = usersRepository;
-        this.metricFactory = metricFactory;
-    }
-
-    public Mono<Void> provisionUser(MailboxSession session) {
-        if (session != null && !usersRepository.isReadOnly() && jmapConfiguration.isUserProvisioningEnabled()) {
-            return createAccountIfNeeded(session);
-        }
-        return Mono.empty();
-    }
-
-    private Mono<Void> createAccountIfNeeded(MailboxSession session) {
-        Username username = session.getUser();
-        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-user-provisioning",
-            needsAccountCreation(username)
-                .filter(FunctionalUtils.identityPredicate())
-                .flatMap(any -> createAccount(username))
-                .onErrorResume(AlreadyExistInUsersRepositoryException.class, e -> Mono.empty())));
-    }
-
-    private Mono<Void> createAccount(Username username) {
-        return Mono.fromRunnable(Throwing.runnable(() -> usersRepository.addUser(username, generatePassword())))
-            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER)
-            .then();
-    }
-
-    private Mono<Boolean> needsAccountCreation(Username username) {
-        return Mono.from(usersRepository.containsReactive(username))
-            .map(FunctionalUtils.negate());
-    }
-
-    private String generatePassword() {
-        return UUID.randomUUID().toString();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/main/resources/jwt-public.der b/server/protocols/jmap-draft/src/main/resources/jwt-public.der
deleted file mode 100644
index e69de29..0000000
--- a/server/protocols/jmap-draft/src/main/resources/jwt-public.der
+++ /dev/null
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/MessageFastViewProjectionItemFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/MessageFastViewProjectionItemFactoryTest.java
deleted file mode 100644
index 40665c2..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/MessageFastViewProjectionItemFactoryTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.model.Preview;
-import org.apache.james.jmap.api.projections.MessageFastViewPrecomputedProperties;
-import org.apache.james.jmap.utils.JsoupHtmlTextExtractor;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.FetchGroup;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.MessageRange;
-import org.apache.james.mailbox.model.MessageResult;
-import org.apache.james.util.ClassLoaderUtils;
-import org.apache.james.util.mime.MessageContentExtractor;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class MessageFastViewProjectionItemFactoryTest {
-    public static final Username BOB = Username.of("bob");
-    MessageFastViewPrecomputedProperties.Factory testee;
-    MailboxManager mailboxManager;
-    MailboxSession session;
-    MessageManager mailbox;
-
-    @BeforeEach
-    void setUp() throws Exception {
-        testee = new MessageFastViewPrecomputedProperties.Factory(
-            new Preview.Factory(new MessageContentExtractor(), new JsoupHtmlTextExtractor()));
-        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
-        session = mailboxManager.createSystemSession(BOB);
-        MailboxId mailboxId = mailboxManager.createMailbox(MailboxPath.inbox(BOB), session).get();
-        mailbox = mailboxManager.getMailbox(mailboxId, session);
-    }
-
-    @Test
-    void fromShouldReturnEmptyWhenNoBodyPart() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult("header: value\r\n"));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.EMPTY)
-                .hasAttachment(false)
-                .build());
-    }
-
-    @Test
-    void fromShouldReturnEmptyWhenEmptyBodyPart() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult("header: value\r\n\r\n"));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.EMPTY)
-                .hasAttachment(false)
-                .build());
-    }
-
-    @Test
-    void fromShouldReturnEmptyWhenBlankBodyPart() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult("header: value\r\n\r\n  \r\n  \r\n"));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.EMPTY)
-                .hasAttachment(false)
-                .build());
-    }
-
-    @Test
-    void fromShouldReturnSanitizedBodyTextValue() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult("header: value\r\n\r\n  \r\nmessage  \r\n"));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.from("message"))
-                .hasAttachment(false)
-                .build());
-    }
-
-    @Test
-    void fromShouldExtractHtml() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult(ClassLoaderUtils.getSystemResourceAsString("fullMessage.eml")));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.from("blabla bloblo"))
-                .hasAttachment()
-                .build());
-    }
-
-    @Test
-    void fromShouldParseAttachmentWhenOnlyAttachment() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult(ClassLoaderUtils.getSystemResourceAsString("oneAttachment.eml")));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.EMPTY)
-                .hasAttachment()
-                .build());
-    }
-
-    @Test
-    void fromShouldIngnoreAttachmentsWhenInlined() throws Exception {
-        MessageFastViewPrecomputedProperties actual = testee.from(toMessageResult(ClassLoaderUtils.getSystemResourceAsString("inlineAttachment.eml")));
-
-        assertThat(actual)
-            .isEqualTo(MessageFastViewPrecomputedProperties.builder()
-                .preview(Preview.from("I'm the body!"))
-                .hasAttachment(false)
-                .build());
-    }
-
-    MessageResult toMessageResult(String messageAsString) throws Exception {
-        ComposedMessageId composedMessageId = mailbox.appendMessage(MessageManager.AppendCommand.builder()
-            .build(messageAsString), session).getId();
-
-        return mailbox.getMessages(MessageRange.one(composedMessageId.getUid()), FetchGroup.FULL_CONTENT, session)
-            .next();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/PreviewFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/PreviewFactoryTest.java
deleted file mode 100644
index 311c5d6..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/PreviewFactoryTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.jmap.api.model.Preview;
-import org.apache.james.jmap.utils.JsoupHtmlTextExtractor;
-import org.apache.james.util.ClassLoaderUtils;
-import org.apache.james.util.mime.MessageContentExtractor;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class PreviewFactoryTest {
-    Preview.Factory testee;
-
-    @BeforeEach
-    void setUp() {
-        testee = new Preview.Factory(new MessageContentExtractor(), new JsoupHtmlTextExtractor());
-    }
-
-    @Test
-    void fromMessageAsStringShouldReturnEmptyWhenNoBodyPart() throws Exception {
-        Preview actual = testee.fromMessageAsString("header: value\r\n");
-
-        assertThat(actual).isEqualTo(Preview.EMPTY);
-    }
-
-    @Test
-    void fromMessageAsStringShouldReturnEmptyWhenEmptyBodyPart() throws Exception {
-        Preview actual = testee.fromMessageAsString("header: value\r\n\r\n");
-
-        assertThat(actual).isEqualTo(Preview.EMPTY);
-    }
-
-    @Test
-    void fromMessageAsStringShouldReturnEmptyWhenBlankBodyPart() throws Exception {
-        Preview actual = testee.fromMessageAsString("header: value\r\n\r\n  \r\n  \r\n");
-
-        assertThat(actual).isEqualTo(Preview.EMPTY);
-    }
-
-    @Test
-    void fromMessageAsStringShouldReturnSanitizedBodyTextValue() throws Exception {
-        Preview actual = testee.fromMessageAsString("header: value\r\n\r\n  \r\nmessage  \r\n");
-
-        assertThat(actual).isEqualTo(Preview.from("message"));
-    }
-
-    @Test
-    void fromMessageAsStringShouldExtractHtml() throws Exception {
-        Preview actual = testee.fromMessageAsString(ClassLoaderUtils.getSystemResourceAsString("fullMessage.eml"));
-
-        assertThat(actual).isEqualTo(Preview.from("blabla bloblo"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BeansTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BeansTest.java
deleted file mode 100644
index 6ec338e..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BeansTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft;
-
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class BeansTest {
-
-    @Test
-    public void beanShouldRespectBeanContract() {
-        EqualsVerifier.forClass(Mailbox.class)
-            .verify();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/FixedDateZonedDateTimeProvider.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/FixedDateZonedDateTimeProvider.java
deleted file mode 100644
index 1a5679e..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/FixedDateZonedDateTimeProvider.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft;
-
-import java.time.ZonedDateTime;
-
-import org.apache.james.util.date.ZonedDateTimeProvider;
-
-public class FixedDateZonedDateTimeProvider implements ZonedDateTimeProvider {
-
-    private ZonedDateTime zonedDateTime;
-
-    public void setFixedDateTime(ZonedDateTime zonedDateTime) {
-        this.zonedDateTime = zonedDateTime;
-    }
-
-    @Override
-    public ZonedDateTime get() {
-        return zonedDateTime;
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPDraftConfigurationTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPDraftConfigurationTest.java
deleted file mode 100644
index 1700cfc..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPDraftConfigurationTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class JMAPDraftConfigurationTest {
-
-    public static final boolean ENABLED = true;
-    public static final boolean DISABLED = false;
-
-    @Test
-    public void buildShouldThrowWhenKeystoreIsNull() {
-        assertThatThrownBy(() -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore(null)
-                .build())
-            .isInstanceOf(IllegalStateException.class)
-            .hasMessage("('keystore' && 'secret') or (privateKey && certificates) is mandatory");
-    }
-
-    @Test
-    public void buildShouldThrowWhenKeystoreIsEmpty() {
-        assertThatThrownBy(() -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore("")
-                .build())
-            .isInstanceOf(IllegalStateException.class)
-            .hasMessage("('keystore' && 'secret') or (privateKey && certificates) is mandatory");
-    }
-
-    @Test
-    public void buildShouldThrowWhenSecretIsNull() {
-        assertThatThrownBy(() -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore("keystore")
-                .secret(null)
-                .build())
-            .isInstanceOf(IllegalStateException.class)
-            .hasMessage("('keystore' && 'secret') or (privateKey && certificates) is mandatory");
-    }
-
-    @Test
-    public void buildShouldThrowWhenSecretIsEmpty() {
-        assertThatThrownBy(() -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore("keystore")
-                .secret("")
-                .build())
-            .isInstanceOf(IllegalStateException.class)
-            .hasMessage("('keystore' && 'secret') or (privateKey && certificates) is mandatory");
-    }
-
-    @Test
-    public void buildShouldThrowWhenJwtPublicKeyPemIsNull() {
-        assertThatThrownBy(() -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore("keystore")
-                .secret("secret")
-                .jwtPublicKeyPem(null)
-                .build())
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void buildShouldNotThrowWhenJwtPublicKeyPemIsEmpty() {
-        assertThatCode(
-            () -> JMAPDraftConfiguration.builder()
-                .enable()
-                .keystore("keystore")
-                .secret("secret")
-                .build())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void buildShouldWorkWhenDisabled() {
-        List<String> jwtPublicKeyPem = ImmutableList.of();
-        Optional<String> privateKey = Optional.empty();
-        Optional<String> certificates = Optional.empty();
-        Optional<String> keystore = Optional.empty();
-        Optional<String> secret = Optional.empty();
-        Optional<List<String>> authenticationStrategies = Optional.empty();
-
-        JMAPDraftConfiguration expectedJMAPDraftConfiguration = new JMAPDraftConfiguration(DISABLED, keystore,
-            privateKey, certificates, "JKS", secret, jwtPublicKeyPem, authenticationStrategies);
-
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .disable()
-            .build();
-        assertThat(jmapDraftConfiguration).isEqualToComparingFieldByField(expectedJMAPDraftConfiguration);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImplTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImplTest.java
deleted file mode 100644
index a69b08b..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/AccessTokenManagerImplTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatNullPointerException;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.AccessTokenRepository;
-import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
-import org.apache.james.jmap.draft.api.AccessTokenManager;
-import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import reactor.core.publisher.Mono;
-
-class AccessTokenManagerImplTest {
-    private static final Username USERNAME = Username.of("username");
-
-    private AccessTokenManager accessTokenManager;
-    private AccessTokenRepository accessTokenRepository;
-    
-    @BeforeEach
-    void setUp() {
-        accessTokenRepository = new MemoryAccessTokenRepository(500);
-        accessTokenManager = new AccessTokenManagerImpl(accessTokenRepository);
-    }
-
-    @Test
-    void grantShouldThrowOnNullUsername() {
-        assertThatNullPointerException()
-            .isThrownBy(() -> accessTokenManager.grantAccessToken(null));
-    }
-    
-    @Test
-    void grantShouldGenerateATokenOnUsername() {
-        assertThat(accessTokenManager.grantAccessToken(USERNAME)).isNotNull();
-    }
-
-    @Test
-    void grantShouldStoreATokenOnUsername() {
-        AccessToken token = Mono.from(accessTokenManager.grantAccessToken(USERNAME)).block();
-        assertThat(accessTokenRepository.getUsernameFromToken(token).block()).isEqualTo(USERNAME);
-    }
-    
-    @Test
-    void getUsernameShouldThrowWhenNullToken() {
-        assertThatNullPointerException()
-            .isThrownBy(() -> Mono.from(accessTokenManager.getUsernameFromToken(null)).block());
-    }
-
-    @Test
-    void getUsernameShouldThrowWhenUnknownToken() {
-        assertThatThrownBy(() -> Mono.from(accessTokenManager.getUsernameFromToken(AccessToken.generate())).block())
-            .isExactlyInstanceOf(InvalidAccessToken.class);
-    }
-
-    @Test
-    void getUsernameShouldThrowWhenOtherToken() {
-        accessTokenManager.grantAccessToken(USERNAME);
-        assertThatThrownBy(() -> Mono.from(accessTokenManager.getUsernameFromToken(AccessToken.generate())).block())
-            .isExactlyInstanceOf(InvalidAccessToken.class);
-    }
-
-    @Test
-    void getUsernameShouldReturnUsernameWhenExistingUsername() {
-        AccessToken token = Mono.from(accessTokenManager.grantAccessToken(USERNAME)).block();
-        assertThat(Mono.from(accessTokenManager.getUsernameFromToken(token)).block()).isEqualTo(USERNAME);
-    }
-    
-    @Test
-    void isValidShouldThrowOnNullToken() {
-        assertThatNullPointerException()
-            .isThrownBy(() -> accessTokenManager.isValid(null));
-    }
-    
-    @Test
-    void isValidShouldReturnFalseOnUnknownToken() {
-        assertThat(Mono.from(accessTokenManager.isValid(AccessToken.generate())).block()).isFalse();
-    }
-    
-    @Test
-    void isValidShouldReturnFalseWhenOtherToken() {
-        accessTokenManager.grantAccessToken(USERNAME);
-        assertThat(Mono.from(accessTokenManager.isValid(AccessToken.generate())).block()).isFalse();
-    }
-    
-    @Test
-    void isValidShouldReturnTrueWhenValidToken() {
-        AccessToken accessToken = Mono.from(accessTokenManager.grantAccessToken(USERNAME)).block();
-        assertThat(Mono.from(accessTokenManager.isValid(accessToken)).block()).isTrue();
-    }
-    
-    @Test
-    void revokeShouldThrowWhenNullToken() {
-        assertThatNullPointerException()
-            .isThrownBy(() -> Mono.from(accessTokenManager.revoke(null)).block());
-    }
-    
-    @Test
-    void revokeShouldNoopOnUnknownToken() {
-        Mono.from(accessTokenManager.revoke(AccessToken.generate())).block();
-    }
-    
-    @Test
-    void revokeShouldNoopOnRevokingTwice() {
-        AccessToken token = AccessToken.generate();
-        Mono.from(accessTokenManager.revoke(token)).block();
-        Mono.from(accessTokenManager.revoke(token)).block();
-    }
-    
-    @Test
-    void revokeShouldInvalidExistingToken() {
-        AccessToken token = Mono.from(accessTokenManager.grantAccessToken(USERNAME)).block();
-        Mono.from(accessTokenManager.revoke(token)).block();
-        assertThat(Mono.from(accessTokenManager.isValid(token)).block()).isFalse();
-    }
-
-    @Test
-    void getUsernameShouldThrowWhenRepositoryThrows() {
-        accessTokenRepository = mock(AccessTokenRepository.class);
-        accessTokenManager = new AccessTokenManagerImpl(accessTokenRepository);
-
-        AccessToken accessToken = AccessToken.generate();
-        when(accessTokenRepository.getUsernameFromToken(accessToken)).thenReturn(Mono.error(new InvalidAccessToken(accessToken)));
-
-        assertThatThrownBy(() -> Mono.from(accessTokenManager.getUsernameFromToken(accessToken)).block())
-            .isExactlyInstanceOf(InvalidAccessToken.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerFixture.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerFixture.java
deleted file mode 100644
index f527079..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerFixture.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import org.apache.james.filesystem.api.FileSystemFixture;
-import org.apache.james.jmap.draft.JMAPDraftConfiguration;
-
-import com.google.common.collect.ImmutableList;
-
-class JamesSignatureHandlerFixture {
-
-    static final String JWT_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" +
-        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
-        "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
-        "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
-        "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
-        "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
-        "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
-        "kwIDAQAB\n" +
-        "-----END PUBLIC KEY-----";
-
-    static JamesSignatureHandler defaultSignatureHandler() {
-
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore("keystore")
-            .secret("james72laBalle")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        return new JamesSignatureHandler(loader);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerTest.java
deleted file mode 100644
index 2948e25..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/JamesSignatureHandlerTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class JamesSignatureHandlerTest {
-
-    public static final String SIGNATURE = "NeIFNei4p6vn085wCEw0pbEwJ+Oak5yEIRLZsDcRVzT9rWWOcLvDFUA3S6awi/bxPiFxqJFreVz6xqzehnUI4tUBupk3sIsqeXShhFWBpaV+m58mC41lT/A0RJa3GgCvg6kmweCRf3tOo0+gvwOQJdwCL2B21GjDCKqBHaiK+OHcsSjrQW0xuew5z84EAz3ErdH4MMNjITksxK5FG/cGQ9V6LQgwcPk0RrprVC4eY7FFHw/sQNlJpZKsSFLnn5igPQkQtjiQ4ay1/xoB7FU7aJLakxRhYOnTKgper/Ur7UWOZJaE+4EjcLwCFLF9GaCILwp9W+mf/f7j92PVEU50Vg==";
-    private static final String FAKE_SIGNATURE = "MeIFNei4p6vn085wCEw0pbEwJ+Oak5yEIRLZsDcRVzT9rWWOcLvDFUA3S6awi/bxPiFxqJFreVz6xqzehnUI4tUBupk3sIsqeXShhFWBpaV+m58mC41lT/A0RJa3GgCvg6kmweCRf3tOo0+gvwOQJdwCL2B21GjDCKqBHaiK+OHcsSjrQW0xuew5z84EAz3ErdH4MMNjITksxK5FG/cGQ9V6LQgwcPk0RrprVC4eY7FFHw/sQNlJpZKsSFLnn5igPQkQtjiQ4ay1/xoB7FU7aJLakxRhYOnTKgper/Ur7UWOZJaE+4EjcLwCFLF9GaCILwp9W+mf/f7j92PVEU50Vg==";
-    public static final String SOURCE = "plop";
-
-    private JamesSignatureHandler signatureHandler;
-
-    @Before
-    public void setUp() throws Exception {
-       signatureHandler = JamesSignatureHandlerFixture.defaultSignatureHandler();
-       signatureHandler.init();
-    }
-
-    @Test
-    public void validSignatureShouldBeRecognised() throws Exception {
-        assertThat(signatureHandler.verify(SOURCE, signatureHandler.sign(SOURCE))).isTrue();
-    }
-
-    @Test
-    public void invalidSignatureShouldNotBeRecognised() throws Exception {
-        assertThat(signatureHandler.verify(SOURCE, signatureHandler.sign(FAKE_SIGNATURE))).isFalse();
-    }
-
-    @Test
-    public void incorrectLengthSignatureShouldReturnFalse() throws Exception {
-        assertThat(signatureHandler.verify(SOURCE, "c2lnbmF0dXJl")).isFalse();
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void signShouldThrowOnNullSource() throws Exception {
-        signatureHandler.sign(null);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void verifyShouldThrowOnNullSource() throws Exception {
-        signatureHandler.verify(null, "signature");
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void verifyShouldThrowOnNullSignature() throws Exception {
-        signatureHandler.verify(SOURCE, null);
-    }
-
-    @Test
-    public void signOutputShouldBeValid() throws Exception {
-        assertThat(signatureHandler.sign(SOURCE))
-            .isEqualTo(SIGNATURE);
-    }
-
-    @Test
-    public void verifyOutputShouldBeValid() throws Exception {
-        assertThat(signatureHandler.verify(SOURCE,
-            SIGNATURE))
-            .isTrue();
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoaderTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoaderTest.java
deleted file mode 100644
index dd398aa..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SecurityKeyLoaderTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import static org.apache.james.jmap.draft.crypto.JamesSignatureHandlerFixture.JWT_PUBLIC_KEY;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.security.KeyStoreException;
-
-import org.apache.james.filesystem.api.FileSystemFixture;
-import org.apache.james.jmap.draft.JMAPDraftConfiguration;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-
-import com.google.common.collect.ImmutableList;
-
-import nl.altindag.ssl.exception.GenericKeyStoreException;
-
-class SecurityKeyLoaderTest {
-    @Test
-    void loadShouldThrowWhenJMAPIsNotEnabled() {
-        JMAPDraftConfiguration jmapConfiguration = JMAPDraftConfiguration.builder()
-            .disable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore("keystore")
-            .secret("james72laBalle")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapConfiguration);
-
-        assertThatThrownBy(loader::load)
-            .isInstanceOf(RuntimeException.class)
-            .hasMessage("JMAP is not enabled");
-    }
-
-    @Test
-    void loadShouldThrowWhenWrongKeystore() {
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore("badAliasKeystore")
-            .secret("password")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        assertThatThrownBy(loader::load)
-            .isInstanceOf(KeyStoreException.class)
-            .hasMessage("Alias 'james' keystore can't be found");
-    }
-
-    @Test
-    void loadShouldThrowWhenWrongPassword() {
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore("keystore")
-            .secret("WrongPassword")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        assertThatThrownBy(loader::load)
-            .isInstanceOf(GenericKeyStoreException.class)
-            .hasMessageContaining("Keystore was tampered with, or password was incorrect");
-    }
-
-    @Test
-    void loadShouldReturnAsymmetricKeysWhenCorrectPassword() throws Exception {
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore("keystore")
-            .secret("james72laBalle")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        assertThat(loader.load())
-            .isNotNull();
-    }
-
-    @Test
-    void loadShouldReturnAsymmetricKeysWhenRawPublicKey() throws Exception {
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .certificates("key.pub")
-            .privateKey("private.nopass.key")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        assertThat(loader.load())
-            .isNotNull();
-    }
-
-    @ParameterizedTest
-    @ValueSource(strings = {
-        "keystoreJava7",
-        "keystoreJava11",
-    })
-    void loadShouldReturnAsymmetricKeysWhenUsingKeyStoreGeneratedByDifferentJavaVersions(
-        String keyStoreInDifferentVersion) throws Exception {
-
-        JMAPDraftConfiguration jmapDraftConfiguration = JMAPDraftConfiguration.builder()
-            .enable()
-            .jwtPublicKeyPem(ImmutableList.of(JWT_PUBLIC_KEY))
-            .keystore(keyStoreInDifferentVersion)
-            .secret("james72laBalle")
-            .build();
-
-        SecurityKeyLoader loader = new SecurityKeyLoader(
-            FileSystemFixture.CLASSPATH_FILE_SYSTEM,
-            jmapDraftConfiguration);
-
-        assertThat(loader.load())
-            .isNotNull();
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenFactoryTest.java
deleted file mode 100644
index 8f3532f..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenFactoryTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import org.apache.james.jmap.draft.FixedDateZonedDateTimeProvider;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SignedTokenFactoryTest {
-
-    private static final String EXPIRATION_DATE_STRING = "2011-12-03T10:15:30+01:00";
-    private static final ZonedDateTime DATE = ZonedDateTime.parse(EXPIRATION_DATE_STRING, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
-    private SignedTokenFactory toKenFactory;
-    private FixedDateZonedDateTimeProvider zonedDateTimeProvider;
-
-    @Before
-    public void setUp() throws Exception {
-        JamesSignatureHandler signatureHandler = JamesSignatureHandlerFixture.defaultSignatureHandler();
-        signatureHandler.init();
-        zonedDateTimeProvider = new FixedDateZonedDateTimeProvider();
-        toKenFactory = new SignedTokenFactory(signatureHandler, zonedDateTimeProvider);
-    }
-
-    @Test
-    public void generateContinuationTokenShouldThrowWhenUsernameIsNull() throws Exception {
-        assertThatThrownBy(() -> toKenFactory.generateContinuationToken(null))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void generateContinuationTokenShouldHaveTheRightOutPut() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        assertThat(toKenFactory.generateContinuationToken("user").serialize())
-            .isEqualTo("user_2011-12-03T10:30:30+01:00_eOvOqTmV3dPrhIkbuQSj2sno3YJMxWl6J1sH1JhwYcaNgMX9twm98/WSF9uyDkvJgvBxFokDr53AbxQ3DsJysB2dAzCC0tUM4u8ZMvl/hQrFXhVCdpVMyHRvixKCxnHsVXAr9g3WMn2vbIVq5i3HPgA6/p9FB1+N4WA06B8ueoCrdxT2w1ITEm8p+QZvje3n1F344SgrqgIYqvt0yUvzxnB24f3ccjAKidlBj4wZkcXgUTMbZ7MdnCbDGbp10+tgJqxiv1S0rXZMeJLJ+vBt5TyqEhsJUmUQ84qctlB4yR5FS+ncbAOyZAxs2dWsHqiQjedb3IR77N7CASzqO2mmVw==");
-    }
-
-    @Test
-    public void generateAttachmentAccessTokenShouldThrowWhenUsernameIsNull() throws Exception {
-        assertThatThrownBy(() -> toKenFactory.generateAttachmentAccessToken(null, "blobId"))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void generateAttachmentAccessTokenShouldThrowWhenBlobIdIsNull() throws Exception {
-        assertThatThrownBy(() -> toKenFactory.generateAttachmentAccessToken("username", null))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void generateAttachmentAccessTokenShouldHaveTheRightOutPut() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        assertThat(toKenFactory.generateAttachmentAccessToken("user", "blobId").serialize())
-            .isEqualTo("user_2011-12-03T10:20:30+01:00_eSg1mVxMpqw5/u6wsTAatP7hoHDoI7blEW0hxGPrRMMj3hECT+YhbUCdhz9Lb4U+jsYPgNLDuAHwxin79xXfLoq0nVsogEE32svRYVvbaDpro+EOtkAHhYnYxWnAGxB/70u7Zyw0oYGmWOwkCkLDFsWKglMp9IUpOJQP50zbzbdW+4dKlAi/8VmN8jFyZx40envRbgEn4Q2QQbnUH/7F9+vdLIl+bAfcj6QlevqFRsUkmTZelkv1rtGUAvnPSBQL4TeBx5Qk/eEiw8IbB2lbCIAoIFZC6Vl8QOO5Y6LFzmqHL9i0BjvuoiZ8FKQS0pGd5CU6pwc7sv0xD82Vx1eFiw==");
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenManagerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenManagerTest.java
deleted file mode 100644
index 12c69ac..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/crypto/SignedTokenManagerTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import org.apache.james.jmap.draft.FixedDateZonedDateTimeProvider;
-import org.apache.james.jmap.draft.api.SimpleTokenManager.TokenStatus;
-import org.apache.james.jmap.draft.model.AttachmentAccessToken;
-import org.apache.james.jmap.draft.model.ContinuationToken;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SignedTokenManagerTest {
-
-    private static final String EXPIRATION_DATE_STRING = "2011-12-03T10:15:30+01:00";
-    private static final String FAKE_SIGNATURE = "MeIFNei4p6vn085wCEw0pbEwJ+Oak5yEIRLZsDcRVzT9rWWOcLvDFUA3S6awi/bxPiFxqJFreVz6xqzehnUI4tUBupk3sIsqeXShhFWBpaV+m58mC41lT/A0RJa3GgCvg6kmweCRf3tOo0+gvwOQJdwCL2B21GjDCKqBHaiK+OHcsSjrQW0xuew5z84EAz3ErdH4MMNjITksxK5FG/cGQ9V6LQgwcPk0RrprVC4eY7FFHw/sQNlJpZKsSFLnn5igPQkQtjiQ4ay1/xoB7FU7aJLakxRhYOnTKgper/Ur7UWOZJaE+4EjcLwCFLF9GaCILwp9W+mf/f7j92PVEU50Vg==";
-    private static final ZonedDateTime DATE = ZonedDateTime.parse(EXPIRATION_DATE_STRING, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
-    private SignedTokenManager tokenManager;
-    private SignedTokenFactory tokenFactory;
-    private FixedDateZonedDateTimeProvider zonedDateTimeProvider;
-
-    @Before
-    public void setUp() throws Exception {
-        JamesSignatureHandler signatureHandler = JamesSignatureHandlerFixture.defaultSignatureHandler();
-        signatureHandler.init();
-        zonedDateTimeProvider = new FixedDateZonedDateTimeProvider();
-        tokenManager = new SignedTokenManager(signatureHandler, zonedDateTimeProvider);
-        tokenFactory = new SignedTokenFactory(signatureHandler, zonedDateTimeProvider);
-
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void isValidShouldThrowWhenTokenIsNull() throws Exception {
-        tokenManager.isValid(null);
-    }
-
-    @Test
-    public void isValidShouldRecognizeValidTokens() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        assertThat(
-            tokenManager.isValid(
-                tokenFactory.generateContinuationToken("user")))
-            .isTrue();
-    }
-
-    @Test
-    public void isValidShouldRecognizeTokenWhereUsernameIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken("pirate",
-            continuationToken.getExpirationDate(),
-            continuationToken.getSignature());
-        assertThat(tokenManager.isValid(pirateContinuationToken)).isFalse();
-    }
-
-    @Test
-    public void isValidShouldRecognizeTokenWhereExpirationDateIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken(continuationToken.getUsername(),
-            continuationToken.getExpirationDate().plusHours(1),
-            continuationToken.getSignature());
-        assertThat(tokenManager.isValid(pirateContinuationToken)).isFalse();
-    }
-
-    @Test
-    public void isValidShouldRecognizeTokenWhereSignatureIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken(continuationToken.getUsername(),
-            continuationToken.getExpirationDate(),
-            FAKE_SIGNATURE);
-        assertThat(tokenManager.isValid(pirateContinuationToken)).isFalse();
-    }
-
-    @Test
-    public void isValidShouldReturnFalseWhenTokenIsOutdated() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        zonedDateTimeProvider.setFixedDateTime(DATE.plusHours(1));
-        assertThat(tokenManager.isValid(continuationToken)).isFalse();
-    }
-
-    @Test
-    public void isValidShouldReturnFalseOnNonValidSignatures() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken pirateContinuationToken = new ContinuationToken("user", DATE.plusMinutes(15), "fake");
-        assertThat(tokenManager.isValid(pirateContinuationToken)).isFalse();
-    }
-
-    @Test
-    public void getValidityShouldThrowWhenTokenIsNull() throws Exception {
-        assertThatThrownBy(() -> tokenManager.getValidity(null))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void getValidityShouldRecognizeValidTokens() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        assertThat(
-            tokenManager.getValidity(
-                tokenFactory.generateContinuationToken("user")))
-            .isEqualTo(TokenStatus.OK);
-    }
-
-    @Test
-    public void getValidityShouldRecognizeTokenWhereUsernameIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken("pirate",
-            continuationToken.getExpirationDate(),
-            continuationToken.getSignature());
-        assertThat(tokenManager.getValidity(pirateContinuationToken)).isEqualTo(TokenStatus.INVALID);
-    }
-
-    @Test
-    public void getValidityhouldRecognizeTokenWhereExpirationDateIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken(continuationToken.getUsername(),
-            continuationToken.getExpirationDate().plusHours(1),
-            continuationToken.getSignature());
-        assertThat(tokenManager.getValidity(pirateContinuationToken)).isEqualTo(TokenStatus.INVALID);
-    }
-
-    @Test
-    public void getValidityShouldRecognizeTokenWhereSignatureIsModified() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        ContinuationToken pirateContinuationToken = new ContinuationToken(continuationToken.getUsername(),
-            continuationToken.getExpirationDate(),
-            FAKE_SIGNATURE);
-        assertThat(tokenManager.getValidity(pirateContinuationToken)).isEqualTo(TokenStatus.INVALID);
-    }
-
-    @Test
-    public void getValidityShouldReturnFalseWhenTokenIsOutdated() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken continuationToken = tokenFactory.generateContinuationToken("user");
-        zonedDateTimeProvider.setFixedDateTime(DATE.plusHours(1));
-        assertThat(tokenManager.getValidity(continuationToken)).isEqualTo(TokenStatus.EXPIRED);
-    }
-
-    @Test
-    public void getValidityShouldReturnFalseOnNonValidSignatures() throws Exception {
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        ContinuationToken pirateContinuationToken = new ContinuationToken("user", DATE.plusMinutes(15), "fake");
-        assertThat(tokenManager.getValidity(pirateContinuationToken)).isEqualTo(TokenStatus.INVALID);
-    }
-
-    @Test
-    public void signedAttachmentAccessTokenShouldBeValidated() {
-        String blobId = "blobId";
-        zonedDateTimeProvider.setFixedDateTime(DATE);
-        String serializedToken = tokenFactory.generateAttachmentAccessToken("user", blobId).serialize();
-
-        assertThat(tokenManager.isValid(AttachmentAccessToken.from(serializedToken, blobId))).isTrue();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/MultipleObjectMapperTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/MultipleObjectMapperTest.java
deleted file mode 100644
index e289cf5..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/MultipleObjectMapperTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class MultipleObjectMapperTest {
-
-    private ObjectMapper mapper;
-
-    @SuppressWarnings("unused")
-    private static class First {
-        public String first;
-        public String other;
-    }
-
-    @SuppressWarnings("unused")
-    private static class Second {
-        public String second;
-        public String other;
-    }
-
-    @Before
-    public void setup() {
-        mapper = new MultipleObjectMapperBuilder()
-                    .registerClass("/first", First.class)
-                    .registerClass("/second", Second.class)
-                    .build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void registeringSameUniquePathShouldThrowAnException() throws Exception {
-        new MultipleObjectMapperBuilder()
-            .registerClass("/other", First.class)
-            .registerClass("/other", Second.class);
-    }
-
-    @Test
-    public void registeringSameClassTwoTimesIsOK() throws Exception {
-        ObjectMapper uselessMapper = new MultipleObjectMapperBuilder()
-                .registerClass("/first", First.class)
-                .registerClass("/other", First.class)
-                .build();
-        String json = "{\"first\": \"value\", \"other\": \"other\"}";
-        Object o = uselessMapper.readValue(json, Object.class);
-        assertThat(o).isInstanceOf(First.class);
-    }
-
-    @Test(expected = JsonMappingException.class)
-    public void badJsonShouldThrowException() throws Exception {
-        String json = "{\"bad\": \"value\"}";
-        mapper.readValue(json, Object.class);
-    }
-
-    @Test
-    public void firstJsonShouldReturnFirstClass() throws Exception {
-        String json = "{\"first\": \"value\", \"other\": \"other\"}";
-        Object o = mapper.readValue(json, Object.class);
-        assertThat(o).isInstanceOf(First.class);
-    }
-
-    @Test
-    public void secondJsonShouldReturnSecondClass() throws Exception {
-        String json = "{\"second\": \"value\", \"other\": \"other\"}";
-        Object o = mapper.readValue(json, Object.class);
-        assertThat(o).isInstanceOf(Second.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/OperatorSerializeDeserializeTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/OperatorSerializeDeserializeTest.java
deleted file mode 100644
index 5b91334..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/OperatorSerializeDeserializeTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.jmap.draft.model.Operator;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.exc.InvalidFormatException;
-
-public class OperatorSerializeDeserializeTest {
-
-    @Test
-    public void deserializeKnownValue() throws Exception {
-        ObjectWithOperator operator = new ObjectMapper().readValue("{\"operator\":\"AND\"}", ObjectWithOperator.class);
-        assertThat(operator.operator).isEqualTo(Operator.AND);
-    }
-
-    @Test(expected = InvalidFormatException.class)
-    public void deserializeUnknownValue() throws Exception {
-        new ObjectMapper().readValue("{\"operator\":\"UNKNOWN\"}", ObjectWithOperator.class);
-    }
-
-    @Test
-    public void serializeKnownValue() throws Exception {
-        ObjectWithOperator objectWithOperator = new ObjectWithOperator();
-        objectWithOperator.operator = Operator.AND;
-        String operator = new ObjectMapper().writeValueAsString(objectWithOperator);
-        assertThat(operator).isEqualTo("{\"operator\":\"AND\"}");
-    }
-
-    private static class ObjectWithOperator {
-
-        public Operator operator;
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjects.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjects.java
deleted file mode 100644
index e8d09ba..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjects.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import java.time.Instant;
-import java.util.Optional;
-
-import jakarta.mail.Flags;
-
-import org.apache.james.jmap.api.model.Preview;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.model.Emailer;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.jmap.model.message.view.SubMessage;
-import org.apache.james.mailbox.FlagsBuilder;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-public interface ParsingWritingObjects {
-
-    interface Common {
-        MessageId MESSAGE_ID = TestMessageId.of(1);
-        BlobId BLOB_ID = BlobId.of("myBlobId");
-        String THREAD_ID = "myThreadId";
-        ImmutableList<MailboxId> MAILBOX_IDS = ImmutableList.of(InMemoryId.of(1), InMemoryId.of(2));
-        String IN_REPLY_TO_MESSAGE_ID = "myInReplyToMessageId";
-        Flags FLAGS = FlagsBuilder.builder()
-                .add(Flags.Flag.FLAGGED, Flags.Flag.ANSWERED, Flags.Flag.DRAFT, Flags.Flag.SEEN)
-                .build();
-        ImmutableSet<Keyword> KEYWORDS = ImmutableSet.of(Keyword.DRAFT, Keyword.FLAGGED, Keyword.ANSWERED, Keyword.SEEN, Keyword.FORWARDED);
-        boolean HAS_ATTACHMENT = true;
-        ImmutableMap<String, String> HEADERS = ImmutableMap.of("h1", "h1Value", "h2", "h2Value");
-        Emailer FROM = Emailer.builder().name("myName").email("myEmail@james.org").build();
-        ImmutableList<Emailer> TO = ImmutableList.of(Emailer.builder().name("to1").email("to1@james.org").build(),
-                Emailer.builder().name("to2").email("to2@james.org").build());
-        ImmutableList<Emailer> CC = ImmutableList.of(Emailer.builder().name("cc1").email("cc1@james.org").build(),
-                Emailer.builder().name("cc2").email("cc2@james.org").build());
-        ImmutableList<Emailer> BCC = ImmutableList.of(Emailer.builder().name("bcc1").email("bcc1@james.org").build(),
-                Emailer.builder().name("bcc2").email("bcc2@james.org").build());
-        ImmutableList<Emailer> REPLY_TO = ImmutableList.of(Emailer.builder().name("replyTo1").email("replyTo1@james.org").build(),
-                Emailer.builder().name("replyTo2").email("replyTo2@james.org").build());
-        String SUBJECT = "mySubject";
-        Instant DATE = Instant.parse("2014-10-30T14:12:00Z");
-        int SIZE = 1024;
-        Preview PREVIEW = Preview.from("myPreview");
-        Optional<String> TEXT_BODY = Optional.of("myTextBody");
-        Optional<String> HTML_BODY = Optional.of("<h1>myHtmlBody</h1>");
-    }
-
-    MessageFullView MESSAGE = MessageFullView.builder()
-            .id(Common.MESSAGE_ID)
-            .blobId(Common.BLOB_ID)
-            .threadId(Common.THREAD_ID)
-            .mailboxIds(Common.MAILBOX_IDS)
-            .inReplyToMessageId(Common.IN_REPLY_TO_MESSAGE_ID)
-            .keywords(Keywords.strictFactory().fromSet(Common.KEYWORDS))
-            .headers(Common.HEADERS)
-            .from(Common.FROM)
-            .to(Common.TO)
-            .cc(Common.CC)
-            .bcc(Common.BCC)
-            .replyTo(Common.REPLY_TO)
-            .subject(Common.SUBJECT)
-            .date(Common.DATE)
-            .size(Common.SIZE)
-            .preview(Common.PREVIEW)
-            .textBody(Common.TEXT_BODY)
-            .htmlBody(Common.HTML_BODY)
-            .hasAttachment(false)
-            .build();
-
-    SubMessage SUB_MESSAGE = SubMessage.builder()
-            .headers(Common.HEADERS)
-            .from(Common.FROM)
-            .to(Common.TO)
-            .cc(Common.CC)
-            .bcc(Common.BCC)
-            .replyTo(Common.REPLY_TO)
-            .subject(Common.SUBJECT)
-            .date(Common.DATE)
-            .textBody(Common.TEXT_BODY)
-            .htmlBody(Common.HTML_BODY)
-            .build();
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjectsTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjectsTest.java
deleted file mode 100644
index 6e4ec19..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/json/ParsingWritingObjectsTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.json;
-
-import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
-import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
-import static org.apache.james.jmap.draft.json.ParsingWritingObjects.MESSAGE;
-import static org.apache.james.jmap.draft.json.ParsingWritingObjects.SUB_MESSAGE;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.nio.charset.StandardCharsets;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.james.jmap.draft.methods.GetMessagesMethod;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.methods.JmapResponseWriterImpl;
-import org.apache.james.jmap.model.message.view.SubMessage;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
-
-public class ParsingWritingObjectsTest {
-    
-    private ObjectMapperFactory testee;
-
-    @Before
-    public void setup() {
-        testee = new ObjectMapperFactory(new InMemoryId.Factory(), new TestMessageId.Factory());
-    }
-
-    @Test
-    public void parsingJsonShouldWorkOnSubMessage() throws Exception {
-        SubMessage expected = SUB_MESSAGE;
-
-        SubMessage subMessage = testee.forParsing()
-            .readValue(IOUtils.toString(ClassLoader.getSystemResource("json/subMessage.json"), StandardCharsets.UTF_8), SubMessage.class);
-
-        assertThat(subMessage).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void writingJsonShouldWorkOnSubMessage() throws Exception {
-        String expected = IOUtils.toString(ClassLoader.getSystemResource("json/subMessage.json"), StandardCharsets.UTF_8);
-
-        String json = testee.forWriting()
-                .writeValueAsString(SUB_MESSAGE);
-
-        assertThatJson(json)
-            .when(IGNORING_ARRAY_ORDER)
-            .isEqualTo(expected);
-
-    }
-
-    @Test
-    public void writingJsonShouldWorkOnMessage() throws Exception {
-        String expected = IOUtils.toString(ClassLoader.getSystemResource("json/message.json"), StandardCharsets.UTF_8);
-
-        SimpleFilterProvider filterProvider = new SimpleFilterProvider()
-                .addFilter(JmapResponseWriterImpl.PROPERTIES_FILTER, SimpleBeanPropertyFilter.serializeAll())
-                .addFilter(GetMessagesMethod.HEADERS_FILTER, SimpleBeanPropertyFilter.serializeAll());
-
-        String json = testee.forWriting()
-                .setFilterProvider(filterProvider)
-                .writeValueAsString(MESSAGE);
-
-        assertThatJson(json)
-            .when(IGNORING_ARRAY_ORDER)
-            .isEqualTo(expected);
-
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/ErrorResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/ErrorResponseTest.java
deleted file mode 100644
index 2759455..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/ErrorResponseTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.junit.Test;
-
-public class ErrorResponseTest {
-
-    @Test
-    public void buildShouldReturnDefaultErrorWhenNoParameter() {
-        ErrorResponse expected = new ErrorResponse(ErrorResponse.DEFAULT_ERROR_MESSAGE, Optional.empty());
-        
-        ErrorResponse actual = ErrorResponse
-                .builder()
-                .build();
-        
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldReturnDefaultErrorWhenNullParameter() {
-        ErrorResponse expected = new ErrorResponse(ErrorResponse.DEFAULT_ERROR_MESSAGE, Optional.empty());
-        
-        ErrorResponse actual = ErrorResponse
-                .builder()
-                .type(null)
-                .build();
-        
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldReturnDefinedErrorWhenTypeParameter() {
-        ErrorResponse expected = new ErrorResponse("my error", Optional.empty());
-        
-        ErrorResponse actual = ErrorResponse
-                .builder()
-                .type("my error")
-                .build();
-        
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldReturnDefinedErrorWhenTypeParameterAndNullDescription() {
-        ErrorResponse expected = new ErrorResponse("my error", Optional.empty());
-        
-        ErrorResponse actual = ErrorResponse
-                .builder()
-                .type("my error")
-                .description(null)
-                .build();
-        
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldReturnDefinedErrorWithDescriptionWhenTypeAndDescriptionParameters() {
-        ErrorResponse expected = new ErrorResponse("my error", Optional.of("custom description"));
-        
-        ErrorResponse actual = ErrorResponse
-                .builder()
-                .type("my error")
-                .description("custom description")
-                .build();
-        
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java
deleted file mode 100644
index 6122ed3..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import jakarta.mail.Flags;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.model.GetMailboxesRequest;
-import org.apache.james.jmap.draft.model.GetMailboxesResponse;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxNamespace;
-import org.apache.james.jmap.draft.model.mailbox.SortOrder;
-import org.apache.james.jmap.http.DefaultMailboxesProvisioner;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.model.Number;
-import org.apache.james.mailbox.DefaultMailboxes;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.MailboxACL;
-import org.apache.james.mailbox.model.MailboxConstants;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-import org.apache.james.mailbox.store.StoreMailboxManager;
-import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.apache.james.mime4j.dom.Message;
-import org.assertj.core.groups.Tuple;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import reactor.core.publisher.Flux;
-
-public class GetMailboxesMethodTest {
-
-    private static final Username USERNAME = Username.of("username@domain.tld");
-    private static final Username USERNAME2 = Username.of("username2@domain.tld");
-
-    private StoreMailboxManager mailboxManager;
-    private GetMailboxesMethod getMailboxesMethod;
-    private MethodCallId methodCallId;
-    private MailboxFactory mailboxFactory;
-
-    private QuotaRootResolver quotaRootResolver;
-    private QuotaManager quotaManager;
-    private DefaultMailboxesProvisioner provisioner;
-
-    @Before
-    public void setup() throws Exception {
-        methodCallId = MethodCallId.of("#0");
-        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
-        quotaRootResolver = mailboxManager.getQuotaComponents().getQuotaRootResolver();
-        quotaManager = mailboxManager.getQuotaComponents().getQuotaManager();
-        mailboxFactory = new MailboxFactory(mailboxManager, quotaManager, quotaRootResolver);
-        provisioner = new DefaultMailboxesProvisioner(mailboxManager, new DefaultMetricFactory());
-
-        getMailboxesMethod = new GetMailboxesMethod(mailboxManager, quotaRootResolver, quotaManager,  mailboxFactory, new DefaultMetricFactory(), provisioner);
-    }
-
-    @Test
-    public void getMailboxesShouldReturnEmptyListWhenNoMailboxes() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .isEmpty();
-    }
-
-    @Test
-    public void getMailboxesShouldNotFailWhenMailboxManagerErrors() throws Exception {
-        StoreMailboxManager mockedMailboxManager = mock(StoreMailboxManager.class);
-        when(mockedMailboxManager.list(any()))
-            .thenReturn(ImmutableList.of(new MailboxPath("namespace", Username.of("user"), "name")));
-        when(mockedMailboxManager.getMailbox(any(MailboxPath.class), any()))
-            .thenThrow(new MailboxException());
-        when(mockedMailboxManager.search(any(), any()))
-            .thenReturn(Flux.empty());
-        GetMailboxesMethod testee = new GetMailboxesMethod(mockedMailboxManager, quotaRootResolver, quotaManager, mailboxFactory, new DefaultMetricFactory(), provisioner);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-        MailboxSession session = MailboxSessionUtil.create(USERNAME);
-
-        List<JmapResponse> getMailboxesResponse = testee.processToStream(getMailboxesRequest, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .isEmpty();
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void getMailboxesShouldReturnMailboxesWhenAvailable() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "name");
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        MessageManager messageManager = mailboxManager.getMailbox(mailboxPath, mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.from(
-            Message.Builder.of()
-                .setSubject("test")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.from(
-            Message.Builder.of()
-                .setSubject("test2")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .extracting(Mailbox::getId, Mailbox::getName, Mailbox::getUnreadMessages)
-                .containsOnly(Tuple.tuple(InMemoryId.of(1), mailboxPath.getName(), Number.fromLong(2L)));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void getMailboxesShouldReturnOnlyMailboxesOfCurrentUserWhenAvailable() throws Exception {
-        MailboxPath mailboxPathToReturn = MailboxPath.forUser(USERNAME, "mailboxToReturn");
-        MailboxPath mailboxPathtoSkip = MailboxPath.forUser(USERNAME2, "mailboxToSkip");
-        MailboxSession userSession = mailboxManager.createSystemSession(USERNAME);
-        MailboxSession user2Session = mailboxManager.createSystemSession(USERNAME2);
-        mailboxManager.createMailbox(mailboxPathToReturn, userSession);
-        mailboxManager.createMailbox(mailboxPathtoSkip, user2Session);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, userSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .extracting(Mailbox::getId, Mailbox::getName)
-                .containsOnly(Tuple.tuple(InMemoryId.of(1), mailboxPathToReturn.getName()));
-    }
-
-    @Test
-    public void getMailboxesShouldReturnInboxWithSortOrder10() throws Exception {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "INBOX");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> mailbox.getName().equalsIgnoreCase("INBOX"))
-                .extracting(Mailbox::getSortOrder)
-                .containsOnly(SortOrder.of(10));
-    }
-
-    @Test
-    public void getMailboxesShouldReturnSortOrder1000OnUnknownFolder() throws Exception {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "unknown");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .extracting(Mailbox::getSortOrder)
-                .containsOnly(SortOrder.of(1000));
-    }
-
-    @Test
-    public void getMailboxesShouldReturnInboxWithSortOrder10OnDifferenceCase() throws Exception {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "InBoX");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> mailbox.getName().equalsIgnoreCase("INBOX"))
-                .extracting(Mailbox::getSortOrder)
-                .containsOnly(SortOrder.of(10));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void getMailboxesShouldReturnMailboxesWithSortOrder() throws Exception {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(MailboxPath.inbox(USERNAME), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Archive"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Drafts"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Outbox"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Sent"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Trash"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Spam"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Templates"), mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .extracting(Mailbox::getName, Mailbox::getSortOrder)
-                .containsExactly(
-                        Tuple.tuple("INBOX", SortOrder.of(10)),
-                        Tuple.tuple("Archive", SortOrder.of(20)),
-                        Tuple.tuple("Drafts", SortOrder.of(30)),
-                        Tuple.tuple("Outbox", SortOrder.of(40)),
-                        Tuple.tuple("Sent", SortOrder.of(50)),
-                        Tuple.tuple("Trash", SortOrder.of(60)),
-                        Tuple.tuple("Spam", SortOrder.of(70)),
-                        Tuple.tuple("Templates", SortOrder.of(80)));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void getMailboxesShouldReturnEmptyMailboxByDefault() throws MailboxException {
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "name");
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .extracting(Mailbox::getTotalMessages, Mailbox::getUnreadMessages)
-                .containsOnly(Tuple.tuple(Number.ZERO, Number.ZERO));
-    }
-
-    @Test
-    public void getMailboxesShouldReturnCorrectTotalMessagesCount() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "name");
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        MessageManager messageManager = mailboxManager.getMailbox(mailboxPath, mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.from(
-            Message.Builder.of()
-                .setSubject("test")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.from(
-            Message.Builder.of()
-                .setSubject("test2")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .extracting(Mailbox::getTotalMessages)
-                .containsExactly(Number.fromLong(2L));
-    }
-
-    @Test
-    public void getMailboxesShouldReturnCorrectUnreadMessagesCount() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "name");
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        MessageManager messageManager = mailboxManager.getMailbox(mailboxPath, mailboxSession);
-        Flags defaultUnseenFlag = new Flags();
-        Flags readMessageFlag = new Flags();
-        readMessageFlag.add(Flags.Flag.SEEN);
-        messageManager.appendMessage(MessageManager.AppendCommand.builder()
-            .withFlags(defaultUnseenFlag)
-            .build(Message.Builder.of()
-                .setSubject("test")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.builder()
-            .withFlags(defaultUnseenFlag)
-            .build(Message.Builder.of()
-                .setSubject("test2")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-        messageManager.appendMessage(MessageManager.AppendCommand.builder()
-            .withFlags(readMessageFlag)
-            .build(Message.Builder.of()
-                .setSubject("test3")
-                .setBody("testmail", StandardCharsets.UTF_8)), mailboxSession);
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .filteredOn(mailbox -> !DefaultMailboxes.DEFAULT_MAILBOXES.contains(mailbox.getName()))
-                .extracting(Mailbox::getUnreadMessages)
-                .containsExactly(Number.fromLong(2L));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void getMailboxesShouldReturnMailboxesWithRoles() throws Exception {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(USERNAME);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "INBOX"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Archive"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Drafts"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Outbox"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Sent"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Trash"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Spam"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Templates"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "Restored-Messages"), mailboxSession);
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "WITHOUT ROLE"), mailboxSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, mailboxSession).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-                .hasSize(1)
-                .extracting(JmapResponse::getResponse)
-                .hasOnlyElementsOfType(GetMailboxesResponse.class)
-                .extracting(GetMailboxesResponse.class::cast)
-                .flatExtracting(GetMailboxesResponse::getList)
-                .extracting(Mailbox::getName, Mailbox::getRole)
-                .containsOnly(
-                        Tuple.tuple("INBOX", Optional.of(Role.INBOX)),
-                        Tuple.tuple("Archive", Optional.of(Role.ARCHIVE)),
-                        Tuple.tuple("Drafts", Optional.of(Role.DRAFTS)),
-                        Tuple.tuple("Outbox", Optional.of(Role.OUTBOX)),
-                        Tuple.tuple("Sent", Optional.of(Role.SENT)),
-                        Tuple.tuple("Trash", Optional.of(Role.TRASH)),
-                        Tuple.tuple("Spam", Optional.of(Role.SPAM)),
-                        Tuple.tuple("Templates", Optional.of(Role.TEMPLATES)),
-                        Tuple.tuple("Restored-Messages", Optional.of(Role.RESTORED_MESSAGES)),
-                        Tuple.tuple("WITHOUT ROLE", Optional.empty()));
-    }
-
-    @Test
-    public void getMailboxesShouldNotExposeRoleOfSharedMailboxToSharee() throws Exception {
-        MailboxSession userSession = mailboxManager.createSystemSession(USERNAME);
-        MailboxSession user2Session = mailboxManager.createSystemSession(USERNAME2);
-
-        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, "INBOX");
-        mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, "INBOX"), userSession);
-
-        MailboxACL.Rfc4314Rights rights = new MailboxACL.Rfc4314Rights(MailboxACL.Right.Lookup);
-        MailboxACL.ACLCommand command = MailboxACL.command().forUser(Username.of(USERNAME2.asString())).rights(rights).asReplacement();
-        mailboxManager.applyRightsCommand(mailboxPath, command, userSession);
-
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-            .build();
-
-        List<JmapResponse> getMailboxesResponse = getMailboxesMethod.processToStream(getMailboxesRequest, methodCallId, user2Session).collect(Collectors.toList());
-
-        assertThat(getMailboxesResponse)
-            .hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMailboxesResponse.class)
-            .extracting(GetMailboxesResponse.class::cast)
-            .flatExtracting(GetMailboxesResponse::getList)
-            .filteredOn(mailbox -> MailboxConstants.INBOX.equals(mailbox.getName()))
-            .filteredOn(mailbox -> !mailbox.getNamespace().equals(MailboxNamespace.personal()))
-            .extracting(Mailbox::getName, Mailbox::getRole)
-            .containsOnly(Tuple.tuple("INBOX", Optional.empty()));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMessagesMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMessagesMethodTest.java
deleted file mode 100644
index 5e73c26..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMessagesMethodTest.java
+++ /dev/null
@@ -1,777 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import jakarta.mail.Flags;
-import jakarta.mail.Flags.Flag;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.model.GetMessagesRequest;
-import org.apache.james.jmap.draft.model.GetMessagesResponse;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.memory.projections.MemoryMessageFastViewProjection;
-import org.apache.james.jmap.model.message.view.MessageFastViewFactory;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory;
-import org.apache.james.jmap.model.message.view.MessageHeaderView;
-import org.apache.james.jmap.model.message.view.MessageHeaderViewFactory;
-import org.apache.james.jmap.model.message.view.MessageMetadataView;
-import org.apache.james.jmap.model.message.view.MessageMetadataViewFactory;
-import org.apache.james.jmap.model.message.view.MetaMessageViewFactory;
-import org.apache.james.jmap.utils.JsoupHtmlTextExtractor;
-import org.apache.james.mailbox.FlagsBuilder;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageManager.AppendCommand;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.store.StoreMailboxManager;
-import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.mime4j.message.BodyPartBuilder;
-import org.apache.james.mime4j.message.MultipartBuilder;
-import org.apache.james.mime4j.stream.RawField;
-import org.apache.james.util.html.HtmlTextExtractor;
-import org.apache.james.util.mime.MessageContentExtractor;
-import org.assertj.core.api.Condition;
-import org.assertj.core.data.MapEntry;
-import org.assertj.core.groups.Tuple;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.jayway.jsonpath.JsonPath;
-
-import reactor.core.publisher.Mono;
-
-public class GetMessagesMethodTest {
-    private static final String FORWARDED = "forwarded";
-    private static final Username ROBERT = Username.of("robert");
-
-    private MessageIdManager messageIdManager;
-    private org.apache.james.mime4j.dom.Message messageContent1;
-    private org.apache.james.mime4j.dom.Message messageContent2;
-    private org.apache.james.mime4j.dom.Message messageContent3;
-    private StoreMailboxManager mailboxManager;
-    private GetMessagesMethod testee;
-    private MailboxSession session;
-    private MailboxPath inboxPath;
-    private MailboxPath customMailboxPath;
-    private MethodCallId methodCallId;
-    private MessageMetadataViewFactory messageMetadataViewFactory;
-
-    @Before
-    public void setup() throws Exception {
-        methodCallId = MethodCallId.of("#0");
-        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
-        HtmlTextExtractor htmlTextExtractor = new JsoupHtmlTextExtractor();
-        BlobManager blobManager = mock(BlobManager.class);
-        InMemoryIntegrationResources resources = InMemoryIntegrationResources.defaultResources();
-        mailboxManager = resources.getMailboxManager();
-
-        session = MailboxSessionUtil.create(ROBERT);
-        inboxPath = MailboxPath.inbox(session);
-        customMailboxPath = new MailboxPath(inboxPath, "custom");
-        mailboxManager.createMailbox(inboxPath, session);
-        mailboxManager.createMailbox(customMailboxPath, session);
-        messageIdManager = resources.getMessageIdManager();
-
-        messageMetadataViewFactory = spy(new MessageMetadataViewFactory(blobManager, messageIdManager));
-        MessageFullViewFactory messageFullViewFactory = new MessageFullViewFactory(blobManager, messageContentExtractor,
-            htmlTextExtractor, messageIdManager,
-            new MemoryMessageFastViewProjection(new RecordingMetricFactory()));
-        MessageFastViewFactory messageFastViewFactory = new MessageFastViewFactory(blobManager, messageIdManager,
-            new MemoryMessageFastViewProjection(new RecordingMetricFactory()), messageFullViewFactory);
-
-        MetaMessageViewFactory metaMessageViewFactory = new MetaMessageViewFactory(
-            messageFullViewFactory,
-            new MessageHeaderViewFactory(blobManager, messageIdManager),
-            messageMetadataViewFactory,
-            messageFastViewFactory);
-        testee = new GetMessagesMethod(metaMessageViewFactory, new DefaultMetricFactory());
-
-        messageContent1 = org.apache.james.mime4j.dom.Message.Builder.of()
-            .setSubject("message 1 subject")
-            .setBody("my message", StandardCharsets.UTF_8)
-            .build();
-
-        messageContent2 = org.apache.james.mime4j.dom.Message.Builder.of()
-            .setSubject("message 2 subject")
-            .setBody("my message", StandardCharsets.UTF_8)
-            .build();
-
-        messageContent3 = org.apache.james.mime4j.dom.Message.Builder.of()
-            .addField(new RawField("Great-Header", "message 3 subject"))
-            .setBody("my message", StandardCharsets.UTF_8)
-            .build();
-    }
-
-    @Test
-    public void processShouldThrowWhenNullRequest() {
-        GetMessagesRequest request = null;
-        assertThatThrownBy(() -> testee.processToStream(request, mock(MethodCallId.class), mock(MailboxSession.class))).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenNullSession() {
-        MailboxSession mailboxSession = null;
-        assertThatThrownBy(() -> testee.processToStream(mock(GetMessagesRequest.class), mock(MethodCallId.class), mailboxSession)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenNullMethodCallId() {
-        MethodCallId methodCallId = null;
-        assertThatThrownBy(() -> testee.processToStream(mock(GetMessagesRequest.class), methodCallId, mock(MailboxSession.class))).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenRequestHasAccountId() {
-        assertThatThrownBy(() -> testee.processToStream(
-            GetMessagesRequest.builder().accountId("abc").build(), mock(MethodCallId.class), mock(MailboxSession.class))).isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void processShouldFetchMessages() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(messageContent1), session).getId();
-        ComposedMessageId message2 = inbox.appendMessage(AppendCommand.from(messageContent2), session).getId();
-        ComposedMessageId message3 = inbox.appendMessage(AppendCommand.from(messageContent3), session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId(),
-                message2.getMessageId(),
-                message3.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getId, MessageFullView::getSubject, MessageFullView::getTextBody)
-            .containsOnly(
-                Tuple.tuple(message1.getMessageId(), "message 1 subject", Optional.of("my message")),
-                Tuple.tuple(message2.getMessageId(), "message 2 subject", Optional.of("my message")),
-                Tuple.tuple(message3.getMessageId(), "", Optional.of("my message")));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void processShouldFetchHtmlMessage() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-        ComposedMessageId message = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setSubject("message 1 subject")
-                    .setBody("my <b>HTML</b> message", "html", StandardCharsets.UTF_8)),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getId, MessageFullView::getHtmlBody)
-            .containsOnly(Tuple.tuple(message.getMessageId(), Optional.of("my <b>HTML</b> message")));
-    }
-
-    @Test
-    public void processShouldReturnOnlyMandatoryPropertiesOnEmptyPropertyList() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(this.messageContent1), session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of())
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getProperties())
-            .isEqualTo(Optional.of(ImmutableSet.of(MessageProperty.id)));
-    }
-
-    @Test
-    public void processShouldReturnAllPropertiesWhenNoPropertyGiven() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(messageContent1), session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getProperties())
-            .isEqualTo(Optional.of(MessageProperty.allOutputProperties()));
-    }
-
-    @Test
-    public void processShouldAddMandatoryPropertiesWhenNotInPropertyList() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(messageContent1), session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of(MessageProperty.subject.asFieldName()))
-            .build();
-
-        Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.subject);
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getProperties())
-            .isEqualTo(Optional.of(expected));
-    }
-
-    @Test
-    public void processShouldReturnTextBodyWhenBodyInPropertyListAndEmptyHtmlBody() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(messageContent1), session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of(MessageProperty.body.asFieldName()))
-            .build();
-
-        Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.textBody);
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getProperties())
-            .isEqualTo(Optional.of(expected));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void processShouldReturnTextBodyWhenEmptyTextBodyAndNotEmptyHtmlBody() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setSubject("message 1 subject")
-                    .setBody("my <b>HTML</b> message", "html", StandardCharsets.UTF_8)),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getId, MessageFullView::getTextBody, MessageFullView::getHtmlBody)
-            .containsOnly(Tuple.tuple(message.getMessageId(), Optional.of("my HTML message"), Optional.of("my <b>HTML</b> message")));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void processShouldReturnEmptyTextBodyAndHtmlBodyWhenThoseAreEmpty() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message = inbox.appendMessage(
-            AppendCommand.from(org.apache.james.mime4j.dom.Message.Builder.of()
-                .setSubject("message 1 subject")
-                .setBody("", "html", StandardCharsets.UTF_8)),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getId, MessageFullView::getTextBody, MessageFullView::getHtmlBody)
-            .containsOnly(Tuple.tuple(message.getMessageId(), Optional.empty(), Optional.of("")));
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void processShouldNotOverrideTextBodyWhenItIsThere() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message = inbox.appendMessage(
-            AppendCommand.from(org.apache.james.mime4j.dom.Message.Builder.of()
-                .setSubject("message subject")
-                .setBody(MultipartBuilder.create()
-                    .setSubType("alternative")
-                    .addBodyPart(BodyPartBuilder.create()
-                        .setBody("My plain message", "plain", StandardCharsets.UTF_8))
-                    .addBodyPart(BodyPartBuilder.create()
-                        .setBody("<a>The </a> <strong>HTML</strong> message", "html", StandardCharsets.UTF_8))
-                    .build())),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getId, MessageFullView::getTextBody, MessageFullView::getHtmlBody)
-            .containsOnly(Tuple.tuple(message.getMessageId(), Optional.of("My plain message"), Optional.of("<a>The </a> <strong>HTML</strong> message")));
-    }
-
-    @Test
-    public void processShouldReturnHeadersFieldWhenSpecificHeadersRequestedInPropertyList() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("headers.from", "headers.heADER2"))
-            .build();
-
-        Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.headers);
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getProperties())
-            .isEqualTo(Optional.of(expected));
-    }
-
-    @Test
-    public void processShouldReturnPropertyFilterWhenFilteringHeadersRequested() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("headers.from", "headers.heADER2"))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result)
-            .hasSize(1)
-            .extracting(JmapResponse::getFilterProvider)
-            .are(new Condition<>(Optional::isPresent, "present"));
-        SimpleFilterProvider actualFilterProvider = result.get(0).getFilterProvider().get().getRight();
-        ObjectMapper objectMapper = new ObjectMapper();
-        objectMapper.registerModule(new JavaTimeModule());
-        objectMapper.setFilterProvider(actualFilterProvider.setDefaultFilter(SimpleBeanPropertyFilter.serializeAll()));
-        String response = objectMapper.writer().writeValueAsString(result.get(0));
-        assertThat(JsonPath.parse(response).<Map<String, String>>read("$.response.list[0].headers")).containsOnly(MapEntry.entry("From", "user@domain.tld"), MapEntry.entry("HEADer2", "Header2Content"));
-    }
-
-    @Test
-    public void processShouldReturnOneMessageWhenMessageInSeveralMailboxes() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        MailboxId customMailboxId = mailboxManager.getMailbox(customMailboxPath, session).getId();
-        messageIdManager.setInMailboxes(message1.getMessageId(),
-            ImmutableList.of(message1.getMailboxId(), customMailboxId),
-            session);
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("mailboxIds"))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        Method.Response response = result.get(0).getResponse();
-        assertThat(response).isInstanceOf(GetMessagesResponse.class);
-        GetMessagesResponse getMessagesResponse = (GetMessagesResponse) response;
-        assertThat(getMessagesResponse.list()).hasSize(1)
-            .hasOnlyElementsOfType(MessageMetadataView.class)
-            .extracting(MessageMetadataView.class::cast)
-            .flatExtracting(MessageMetadataView::getMailboxIds)
-            .containsOnly(customMailboxId, message1.getMailboxId());
-    }
-
-    @Test
-    public void processShouldReturnMetadataWhenOnlyMailboxIds() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        MailboxId customMailboxId = mailboxManager.getMailbox(customMailboxPath, session).getId();
-        messageIdManager.setInMailboxes(message1.getMessageId(),
-            ImmutableList.of(message1.getMailboxId(), customMailboxId),
-            session);
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("mailboxIds"))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        Method.Response response = result.get(0).getResponse();
-        assertThat(response).isInstanceOf(GetMessagesResponse.class);
-        GetMessagesResponse getMessagesResponse = (GetMessagesResponse) response;
-        assertThat(getMessagesResponse.list())
-            .hasSize(1)
-            .hasOnlyElementsOfType(MessageMetadataView.class);
-    }
-
-    @Test
-    public void processShouldReturnFullViewWhenRequestedTextBody() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        MailboxId customMailboxId = mailboxManager.getMailbox(customMailboxPath, session).getId();
-        messageIdManager.setInMailboxes(message1.getMessageId(),
-            ImmutableList.of(message1.getMailboxId(), customMailboxId),
-            session);
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("mailboxIds", "textBody"))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        Method.Response response = result.get(0).getResponse();
-        assertThat(response).isInstanceOf(GetMessagesResponse.class);
-        GetMessagesResponse getMessagesResponse = (GetMessagesResponse) response;
-        assertThat(getMessagesResponse.list())
-            .hasSize(1)
-            .hasOnlyElementsOfType(MessageFullView.class);
-    }
-
-    @Test
-    public void processShouldReturnHeaderViewWhenRequestedTo() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.from(
-                org.apache.james.mime4j.dom.Message.Builder.of()
-                    .setFrom("user@domain.tld")
-                    .setField(new RawField("header1", "Header1Content"))
-                    .setField(new RawField("HEADer2", "Header2Content"))
-                    .setSubject("message 1 subject")
-                    .setBody("my message", StandardCharsets.UTF_8)),
-            session).getId();
-
-        MailboxId customMailboxId = mailboxManager.getMailbox(customMailboxPath, session).getId();
-        messageIdManager.setInMailboxes(message1.getMessageId(),
-            ImmutableList.of(message1.getMailboxId(), customMailboxId),
-            session);
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .properties(ImmutableList.of("mailboxIds", "to"))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1);
-        Method.Response response = result.get(0).getResponse();
-        assertThat(response).isInstanceOf(GetMessagesResponse.class);
-        GetMessagesResponse getMessagesResponse = (GetMessagesResponse) response;
-        assertThat(getMessagesResponse.list())
-            .hasSize(1)
-            .hasOnlyElementsOfType(MessageHeaderView.class);
-    }
-
-    @Test
-    public void processShouldNotFailOnSingleMessageFailure() throws Exception {
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        org.apache.james.mime4j.dom.Message messageContent = org.apache.james.mime4j.dom.Message.Builder.of()
-            .setFrom("user@domain.tld")
-            .setField(new RawField("header1", "Header1Content"))
-            .setField(new RawField("HEADer2", "Header2Content"))
-            .setSubject("message 1 subject")
-            .setBody("my message", StandardCharsets.UTF_8)
-            .build();
-
-        ComposedMessageId message1 = inbox.appendMessage(AppendCommand.from(messageContent), session).getId();
-        ComposedMessageId message2 = inbox.appendMessage(AppendCommand.from(messageContent), session).getId();
-
-        doCallRealMethod()
-            .doReturn(Mono.error(new RuntimeException()))
-            .when(messageMetadataViewFactory)
-            .fromMessageResults(any());
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId(), message2.getMessageId()))
-            .properties(ImmutableList.of("mailboxIds"))
-            .build();
-
-        List<JmapResponse> responses = testee.processToStream(request, methodCallId, session).collect(ImmutableList.toImmutableList());
-
-        assertThat(responses).hasSize(1);
-        Method.Response response = responses.get(0).getResponse();
-        assertThat(response).isInstanceOf(GetMessagesResponse.class);
-        GetMessagesResponse getMessagesResponse = (GetMessagesResponse) response;
-        assertThat(getMessagesResponse.list()).hasSize(1);
-    }
-
-    @Test
-    public void processShouldReturnKeywordsForMessageFlags() throws Exception {
-        Flags flags = FlagsBuilder.builder()
-            .add(Flag.ANSWERED, Flag.DRAFT)
-            .build();
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags)
-                .build(messageContent1),
-            session).getId();
-        ComposedMessageId message2 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags)
-                .build(messageContent2),
-            session).getId();
-        ComposedMessageId message3 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags)
-                .build(messageContent3),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId(),
-                message2.getMessageId(),
-                message3.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getKeywords)
-            .containsOnly( ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true),
-                ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true),
-                ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true));
-
-    }
-
-
-    @Test
-    public void processShouldReturnKeywordsWithoutUnsupportedKeywordsForMessageFlags() throws Exception {
-        Flags flags1 = FlagsBuilder.builder()
-            .add(Flag.ANSWERED, Flag.DRAFT, Flag.DELETED)
-            .build();
-        Flags flags2 = FlagsBuilder.builder()
-            .add(Flag.ANSWERED, Flag.DRAFT)
-            .build();
-        Flags flags3 = FlagsBuilder.builder()
-            .add(Flag.ANSWERED, Flag.DRAFT, Flag.RECENT)
-            .build();
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags1)
-                .build(messageContent1),
-            session).getId();
-        ComposedMessageId message2 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags2)
-                .build(messageContent2),
-            session).getId();
-        ComposedMessageId message3 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags3)
-                .build(messageContent3),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId(),
-                message2.getMessageId(),
-                message3.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getKeywords)
-            .containsOnly(ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true),
-                ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true),
-                ImmutableMap.of(
-                    "$Answered", true,
-                    "$Draft", true));
-
-    }
-
-    @Test
-    public void processShouldReturnKeywordsWithoutForwardedWhenForwardedUserFlagsMessages() throws Exception {
-        Flags flags = FlagsBuilder.builder()
-            .add(Flag.ANSWERED, Flag.DELETED)
-            .add(FORWARDED)
-            .build();
-        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
-
-        ComposedMessageId message1 = inbox.appendMessage(
-            AppendCommand.builder()
-                .withFlags(flags)
-                .build(messageContent1),
-            session).getId();
-
-        GetMessagesRequest request = GetMessagesRequest.builder()
-            .ids(ImmutableList.of(message1.getMessageId()))
-            .build();
-
-        List<JmapResponse> result = testee.processToStream(request, methodCallId, session).collect(Collectors.toList());
-
-        assertThat(result).hasSize(1)
-            .extracting(JmapResponse::getResponse)
-            .hasOnlyElementsOfType(GetMessagesResponse.class)
-            .extracting(GetMessagesResponse.class::cast)
-            .flatExtracting(GetMessagesResponse::list)
-            .hasOnlyElementsOfType(MessageFullView.class)
-            .extracting(MessageFullView.class::cast)
-            .extracting(MessageFullView::getKeywords)
-            .containsOnly(ImmutableMap.of(
-                "$Answered", true,
-                FORWARDED, true));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethodTest.java
deleted file mode 100644
index 879662a..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethodTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.utils.AccountIdUtil.toVacationAccountId;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.time.ZonedDateTime;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.model.AccountId;
-import org.apache.james.jmap.draft.model.GetMailboxesRequest;
-import org.apache.james.jmap.draft.model.GetVacationRequest;
-import org.apache.james.jmap.draft.model.GetVacationResponse;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.VacationResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.apache.james.util.date.ZonedDateTimeProvider;
-import org.apache.james.vacation.api.Vacation;
-import org.apache.james.vacation.api.VacationService;
-import org.junit.Before;
-import org.junit.Test;
-
-import reactor.core.publisher.Mono;
-
-public class GetVacationResponseMethodTest {
-
-    private static final ZonedDateTime DATE_2014 = ZonedDateTime.parse("2014-09-30T14:10:00Z");
-    private static final ZonedDateTime DATE_2015 = ZonedDateTime.parse("2015-09-30T14:10:00Z");
-    private static final ZonedDateTime DATE_2016 = ZonedDateTime.parse("2016-09-30T14:10:00Z");
-
-    public static final String USERNAME = "username";
-    private GetVacationResponseMethod testee;
-    private VacationService vacationService;
-    private MailboxSession mailboxSession;
-    private Username username;
-    private ZonedDateTimeProvider zonedDateTimeProvider;
-
-    @Before
-    public void setUp() {
-        zonedDateTimeProvider = mock(ZonedDateTimeProvider.class);
-        vacationService = mock(VacationService.class);
-        mailboxSession = mock(MailboxSession.class);
-        username = Username.of(USERNAME);
-        testee = new GetVacationResponseMethod(vacationService, zonedDateTimeProvider, new DefaultMetricFactory());
-
-        when(zonedDateTimeProvider.get()).thenReturn(DATE_2014);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullRequest() {
-        testee.process(null, mock(MethodCallId.class), mock(MailboxSession.class));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullMethodCallId() {
-        testee.process(mock(GetMailboxesRequest.class), null, mock(MailboxSession.class));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullMailboxSession() {
-        testee.process(mock(GetMailboxesRequest.class), mock(MethodCallId.class), null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void processShouldThrowOnWrongRequestType() {
-        testee.process(mock(SetMailboxesRequest.class), mock(MethodCallId.class), mock(MailboxSession.class));
-    }
-
-    @Test
-    public void processShouldReturnTheAppropriateVacationResponse() {
-        MethodCallId methodCallId = mock(MethodCallId.class);
-        Vacation vacation = Vacation.builder()
-            .enabled(true)
-            .textBody("I am in vacation")
-            .subject(Optional.of("subject"))
-            .fromDate(Optional.of(DATE_2014))
-            .toDate(Optional.of(DATE_2016))
-            .build();
-        when(vacationService.retrieveVacation(toVacationAccountId(AccountId.fromString(USERNAME)))).thenReturn(Mono.just(vacation));
-        when(mailboxSession.getUser()).thenReturn(username);
-        when(zonedDateTimeProvider.get()).thenReturn(DATE_2015);
-
-        GetVacationRequest getVacationRequest = GetVacationRequest.builder().build();
-
-        Stream<JmapResponse> result = testee.processToStream(getVacationRequest, methodCallId, mailboxSession);
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(GetVacationResponseMethod.RESPONSE_NAME)
-            .response(GetVacationResponse.builder()
-                .accountId(USERNAME)
-                .vacationResponse(VacationResponse.builder()
-                    .fromVacation(vacation)
-                    .activated(true)
-                    .build())
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-    }
-
-    @Test
-    public void processShouldReturnUnActivatedVacationResponseWhenBeforeDate() {
-        MethodCallId methodCallId = mock(MethodCallId.class);
-        Vacation vacation = Vacation.builder()
-            .enabled(true)
-            .textBody("I am in vacation")
-            .subject(Optional.of("subject"))
-            .fromDate(Optional.of(DATE_2015))
-            .toDate(Optional.of(DATE_2016))
-            .build();
-        when(vacationService.retrieveVacation(toVacationAccountId(AccountId.fromString(USERNAME)))).thenReturn(Mono.just(vacation));
-        when(mailboxSession.getUser()).thenReturn(username);
-        when(zonedDateTimeProvider.get()).thenReturn(DATE_2014);
-
-        GetVacationRequest getVacationRequest = GetVacationRequest.builder().build();
-
-        Stream<JmapResponse> result = testee.processToStream(getVacationRequest, methodCallId, mailboxSession);
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(GetVacationResponseMethod.RESPONSE_NAME)
-            .response(GetVacationResponse.builder()
-                .accountId(USERNAME)
-                .vacationResponse(VacationResponse.builder()
-                    .fromVacation(vacation)
-                    .activated(false)
-                    .build())
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-    }
-
-
-
-    @Test
-    public void processShouldReturnUnActivatedVacationResponseWhenAfterDate() {
-        MethodCallId methodCallId = mock(MethodCallId.class);
-        Vacation vacation = Vacation.builder()
-            .enabled(true)
-            .textBody("I am in vacation")
-            .subject(Optional.of("subject"))
-            .fromDate(Optional.of(DATE_2014))
-            .toDate(Optional.of(DATE_2015))
-            .build();
-        when(vacationService.retrieveVacation(toVacationAccountId(AccountId.fromString(USERNAME)))).thenReturn(Mono.just(vacation));
-        when(mailboxSession.getUser()).thenReturn(username);
-        when(zonedDateTimeProvider.get()).thenReturn(DATE_2016);
-
-        GetVacationRequest getVacationRequest = GetVacationRequest.builder().build();
-
-        Stream<JmapResponse> result = testee.processToStream(getVacationRequest, methodCallId, mailboxSession);
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(GetVacationResponseMethod.RESPONSE_NAME)
-            .response(GetVacationResponse.builder()
-                .accountId(USERNAME)
-                .vacationResponse(VacationResponse.builder()
-                    .fromVacation(vacation)
-                    .activated(false)
-                    .build())
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapRequestParserImplTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapRequestParserImplTest.java
deleted file mode 100644
index a61da6c..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapRequestParserImplTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class JmapRequestParserImplTest {
-    private JmapRequestParserImpl testee;
-
-    @Before
-    public void setup() {
-        testee = new JmapRequestParserImpl(new ObjectMapperFactory(new InMemoryId.Factory(), new InMemoryMessageId.Factory()));
-    }
-    
-    @Test
-    public void extractJmapRequestShouldThrowWhenNullRequestClass() {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{\"id\": \"id\"}"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        assertThatThrownBy(() -> testee.extractJmapRequest(InvocationRequest.deserialize(nodes), null))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void extractJmapRequestShouldNotThrowWhenJsonContainsUnknownProperty() throws Exception {
-        ObjectNode parameters = new ObjectNode(new JsonNodeFactory(false));
-        parameters.put("id", "myId");
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-                parameters,
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        testee.extractJmapRequest(InvocationRequest.deserialize(nodes), RequestClass.class);
-    }
-
-    @Test
-    public void extractJmapRequestShouldNotThrowWhenPropertyMissingInJson() throws Exception {
-        ObjectNode parameters = new ObjectNode(new JsonNodeFactory(false));
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-                parameters,
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        testee.extractJmapRequest(InvocationRequest.deserialize(nodes), RequestClass.class);
-    }
-
-    private static class RequestClass implements JmapRequest {
-
-        @SuppressWarnings("unused")
-        public String parameter;
-    
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapResponseWriterImplTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapResponseWriterImplTest.java
deleted file mode 100644
index f0164ff..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/JmapResponseWriterImplTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.JmapResponseWriterImpl;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.model.Property;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import com.fasterxml.jackson.annotation.JsonFilter;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
-import reactor.core.publisher.Flux;
-
-public class JmapResponseWriterImplTest {
-    private JmapResponseWriterImpl testee;
-
-    @Before
-    public void setup() {
-        testee = new JmapResponseWriterImpl(new ObjectMapperFactory(new InMemoryId.Factory(), new InMemoryMessageId.Factory()));
-    }
-
-    @Ignore
-    @Test(expected = IllegalStateException.class)
-    public void formatMethodResponseShouldWorkWhenNullJmapResponse() {
-        String expectedMethod = "nwonMethod";
-        String expectedMethodCallId = "#1";
-        String expectedId = "myId";
-
-        Stream<InvocationResponse> response = testee.formatMethodResponse(Flux.just(JmapResponse
-            .builder()
-            .methodCallId(MethodCallId.of(expectedMethodCallId))
-            .response(null)
-            .build()))
-            .toStream();
-
-        List<InvocationResponse> responseList = response.collect(Collectors.toList());
-        assertThat(responseList).hasSize(1)
-            .extracting(InvocationResponse::getResponseName, x -> x.getResults().get("id").asText(), InvocationResponse::getMethodCallId)
-            .containsExactly(tuple(expectedMethod, expectedId, expectedMethodCallId));
-    }
-
-    @Test
-    public void formatMethodResponseShouldWork() {
-        String expectedMethodCallId = "#1";
-        String expectedId = "myId";
-
-        ResponseClass responseClass = new ResponseClass();
-        responseClass.id = expectedId;
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .responseName(Method.Response.name("unknownMethod"))
-                .methodCallId(MethodCallId.of(expectedMethodCallId))
-                .response(responseClass)
-                .build()))
-            .collectList()
-            .block();
-
-        assertThat(response).hasSize(1)
-            .extracting(InvocationResponse::getResponseName, x -> x.getResults().get("id").asText(), InvocationResponse::getMethodCallId)
-            .containsExactly(tuple(Method.Response.name("unknownMethod"), expectedId, MethodCallId.of(expectedMethodCallId)));
-    }
-
-    private static class ResponseClass implements Method.Response {
-
-        @SuppressWarnings("unused")
-        public String id;
-
-    }
-
-    @Test
-    public void formatMethodResponseShouldFilterFieldsWhenProperties() {
-        ObjectResponseClass responseClass = new ObjectResponseClass();
-        responseClass.list = ImmutableList.of(new ObjectResponseClass.Foo("id", "name"));
-        Property property = () -> "id";
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .responseName(Method.Response.name("unknownMethod"))
-                .methodCallId(MethodCallId.of("#1"))
-                .properties(ImmutableSet.of(property))
-                .response(responseClass)
-                .build()))
-            .collectList()
-            .block();
-
-        assertThat(response).hasSize(1);
-        JsonNode firstObject = Iterables.getOnlyElement(response).getResults().get("list").elements().next();
-        assertThat(firstObject.get("id").asText()).isEqualTo("id");
-        assertThat(firstObject.get("name")).isNull();
-    }
-
-
-    @Test
-    public void formatMethodResponseShouldNotFilterFieldsWhenSecondCallWithoutProperties() {
-        ObjectResponseClass responseClass = new ObjectResponseClass();
-        responseClass.list = ImmutableList.of(new ObjectResponseClass.Foo("id", "name"));
-        Property property = () -> "id";
-
-        @SuppressWarnings("unused")
-        Stream<InvocationResponse> ignoredResponse = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .responseName(Method.Response.name("unknownMethod"))
-                .methodCallId(MethodCallId.of("#1"))
-                .properties(ImmutableSet.of(property))
-                .response(responseClass)
-                .build()))
-            .toStream();
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .responseName(Method.Response.name("unknownMethod"))
-                .methodCallId(MethodCallId.of("#1"))
-                .response(responseClass)
-                .build()))
-            .collect(Collectors.toList())
-            .block();
-
-        assertThat(response).hasSize(1);
-        JsonNode firstObject = Iterables.getOnlyElement(response).getResults().get("list").elements().next();
-        assertThat(firstObject.get("id").asText()).isEqualTo("id");
-        assertThat(firstObject.get("name").asText()).isEqualTo("name");
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void formatMethodResponseShouldFilterRightFieldsForEachResponse() {
-        ObjectResponseClass responseClass = new ObjectResponseClass();
-        responseClass.list = ImmutableList.of(new ObjectResponseClass.Foo("id", "name"));
-        Property idProperty = () -> "id";
-        Property nameProperty = () -> "name";
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                    .builder()
-                    .responseName(Method.Response.name("unknownMethod"))
-                    .methodCallId(MethodCallId.of("#1"))
-                    .properties(ImmutableSet.of(idProperty, nameProperty))
-                    .response(responseClass)
-                    .build(),
-                JmapResponse
-                    .builder()
-                    .responseName(Method.Response.name("unknownMethod"))
-                    .methodCallId(MethodCallId.of("#1"))
-                    .properties(ImmutableSet.of(idProperty))
-                    .response(responseClass)
-                    .build()))
-            .collectList()
-            .block();
-
-        assertThat(response).hasSize(2)
-            .extracting(x -> x.getResults().get("list").elements().next())
-            .extracting(
-                x -> x.get("id").asText(),
-                x -> Optional.ofNullable(x.get("name")).map(JsonNode::asText).orElse(null))
-            .containsExactly(tuple("id", "name"), tuple("id", null));
-    }
-
-    @SuppressWarnings("unused")
-    private static class ObjectResponseClass implements Method.Response {
-        @JsonFilter("propertiesFilter")
-        private static class Foo {
-            public String id;
-            public String name;
-
-            public Foo(String id, String name) {
-                this.id = id;
-                this.name = name;
-            }
-        }
-
-        public List<Foo> list;
-    }
-
-    @Test
-    public void formatErrorResponseShouldWork() {
-        String expectedMethodCallId = "#1";
-
-        ObjectNode parameters = new ObjectNode(new JsonNodeFactory(false));
-        parameters.put("id", "myId");
-        JsonNode[] nodes = new JsonNode[]{new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-            parameters,
-            new ObjectNode(new JsonNodeFactory(false)).textNode(expectedMethodCallId)};
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .methodCallId(InvocationRequest.deserialize(nodes).getMethodCallId())
-                .error()
-                .build()))
-            .collectList()
-            .block();
-
-        assertThat(response).hasSize(1)
-            .extracting(InvocationResponse::getResponseName, x -> x.getResults().get("type").asText(), InvocationResponse::getMethodCallId)
-            .containsExactly(tuple(ErrorResponse.ERROR_METHOD, ErrorResponse.DEFAULT_ERROR_MESSAGE, MethodCallId.of(expectedMethodCallId)));
-    }
-
-    @Test
-    public void formatErrorResponseShouldWorkWithTypeAndDescription() {
-        String expectedMethodCallId = "#1";
-
-        ObjectNode parameters = new ObjectNode(new JsonNodeFactory(false));
-        parameters.put("id", "myId");
-        JsonNode[] nodes = new JsonNode[]{new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-            parameters,
-            new ObjectNode(new JsonNodeFactory(false)).textNode(expectedMethodCallId)};
-
-        List<InvocationResponse> response = testee.formatMethodResponse(
-            Flux.just(JmapResponse
-                .builder()
-                .methodCallId(InvocationRequest.deserialize(nodes).getMethodCallId())
-                .error(ErrorResponse
-                    .builder()
-                    .type("errorType")
-                    .description("complete description")
-                    .build())
-                .build()))
-            .collectList()
-            .block();
-
-        assertThat(response).hasSize(1)
-            .extracting(InvocationResponse::getResponseName, x -> x.getResults().get("type").asText(), x -> x.getResults().get("description").asText(), InvocationResponse::getMethodCallId)
-            .containsExactly(tuple(ErrorResponse.ERROR_METHOD, "errorType", "complete description", MethodCallId.of(expectedMethodCallId)));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MIMEMessageConverterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MIMEMessageConverterTest.java
deleted file mode 100644
index d972f9b..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MIMEMessageConverterTest.java
+++ /dev/null
@@ -1,1182 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.io.ByteArrayInputStream;
-import java.nio.charset.StandardCharsets;
-import java.sql.Date;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.methods.ValueWithId.MessageWithId;
-import org.apache.james.jmap.model.Attachment;
-import org.apache.james.jmap.model.Blob;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessage.DraftEmailer;
-import org.apache.james.jmap.draft.model.CreationMessageId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.model.StringBackedAttachmentId;
-import org.apache.james.mime4j.dom.Entity;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.Multipart;
-import org.apache.james.mime4j.dom.SingleBody;
-import org.apache.james.mime4j.dom.TextBody;
-import org.apache.james.mime4j.dom.address.Mailbox;
-import org.apache.james.mime4j.dom.field.ContentTypeField;
-import org.apache.james.mime4j.message.BasicBodyFactory;
-import org.apache.james.mime4j.stream.Field;
-import org.assertj.core.data.Index;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-class MIMEMessageConverterTest {
-    MailboxSession session;
-
-    @BeforeEach
-    void setUp() {
-        session = MailboxSessionUtil.create(Username.of("bob"));
-    }
-
-    @Test
-    void convertToMimeShouldAddInReplyToHeaderWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        String matchingMessageId = "unique-message-id";
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("sender").build())
-                .inReplyToMessageId(matchingMessageId)
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("In-Reply-To")).extracting(Field::getBody)
-                .containsOnly(matchingMessageId);
-    }
-
-    @Test
-    void convertToMimeShouldGenerateMessageId() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage message = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("Message-ID")).extracting(Field::getBody)
-                .isNotNull();
-    }
-
-    @Test
-    void convertToMimeShouldGenerateMessageIdWhenSenderWithoutDomain() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage message = CreationMessage.builder()
-                .from(DraftEmailer.builder().email("sender").build())
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("Message-ID")).extracting(Field::getBody)
-                .isNotNull();
-    }
-
-    @Test
-    void convertToMimeShouldGenerateMessageIdContainingSenderDomain() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage message = CreationMessage.builder()
-                .from(DraftEmailer.builder().email("email@domain.com").build())
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("Message-ID")).hasSize(1);
-        assertThat(result.getHeader().getFields("Message-ID").get(0).getBody())
-            .contains("@domain.com");
-    }
-
-    @Test
-    void convertToMimeShouldAddHeaderWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("sender").build())
-                .headers(ImmutableMap.of("FIRST", "first value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody)
-                .containsOnly("first value");
-    }
-
-    @Test
-    void convertToMimeShouldAddHeadersWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("sender").build())
-                .headers(ImmutableMap.of("FIRST", "first value", "SECOND", "second value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody)
-                .containsOnly("first value");
-        assertThat(result.getHeader().getFields("SECOND")).extracting(Field::getBody)
-            .containsOnly("second value");
-    }
-
-    @Test
-    void convertToMimeShouldFilterGeneratedHeadersWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        String joesEmail = "joe@example.com";
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().email(joesEmail).name("joe").build())
-                .headers(ImmutableMap.of("From", "hacker@example.com", "VALID", "valid header value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getFrom()).extracting(Mailbox::getAddress)
-            .allMatch(f -> f.equals(joesEmail));
-        assertThat(result.getHeader().getFields("VALID")).extracting(Field::getBody)
-            .containsOnly("valid header value");
-        assertThat(result.getHeader().getFields("From")).extracting(Field::getBody)
-            .containsOnly("joe <joe@example.com>");
-    }
-
-    @Test
-    void convertToMimeShouldFilterGeneratedHeadersRegardlessOfCaseWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        String joesEmail = "joe@example.com";
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().email(joesEmail).name("joe").build())
-                .headers(ImmutableMap.of("frOM", "hacker@example.com", "VALID", "valid header value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getFrom()).extracting(Mailbox::getAddress)
-            .allMatch(f -> f.equals(joesEmail));
-        assertThat(result.getHeader().getFields("VALID")).extracting(Field::getBody)
-            .containsOnly("valid header value");
-        assertThat(result.getHeader().getFields("From")).extracting(Field::getBody)
-            .containsOnly("joe <joe@example.com>");
-    }
-
-    @Test
-    void convertToMimeShouldAddMultivaluedHeadersWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("sender").build())
-                .headers(ImmutableMap.of("FIRST", "first value\nsecond value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody)
-            .containsOnly("first value", "second value");
-    }
-
-    @Test
-    void convertToMimeShouldFilterEmptyHeaderNames() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("joe").build())
-                .headers(ImmutableMap.of("", "empty header name value"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("")).isEmpty();
-    }
-
-    @Test
-    void convertToMimeShouldFilterWhiteSpacesOnlyHeaderNames() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage messageHavingInReplyTo = CreationMessage.builder()
-                .from(DraftEmailer.builder().name("joe").build())
-                .headers(ImmutableMap.of("   ", "only spaces header name values"))
-                .mailboxIds(ImmutableList.of("dead-beef-1337"))
-                .subject("subject")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader().getFields("   ")).isEmpty();
-        assertThat(result.getHeader().getFields("")).isEmpty();
-    }
-
-    @Test
-    void convertToMimeShouldThrowWhenMessageIsNull() {
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        assertThatThrownBy(() -> sut.convertToMime(
-                new ValueWithId.CreationMessageEntry(CreationMessageId.of("any"), null),
-                ImmutableList.of()))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    void convertToMimeShouldSetBothFromAndSenderHeaders() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        String joesEmail = "joe@example.com";
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("deadbeef-dead-beef-dead-beef"))
-                .subject("subject")
-                .from(DraftEmailer.builder().email(joesEmail).name("joe").build())
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getFrom()).extracting(Mailbox::getAddress).allMatch(f -> f.equals(joesEmail));
-        assertThat(result.getSender().getAddress()).isEqualTo(joesEmail);
-    }
-
-    @Test
-    void convertToMimeShouldSetCorrectLocalDate() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        Instant now = Instant.now();
-        ZonedDateTime messageDate = ZonedDateTime.ofInstant(now, ZoneId.systemDefault());
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .date(messageDate)
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getDate()).isEqualToIgnoringMillis(Date.from(now));
-    }
-
-    @Test
-    void convertToMimeShouldSetQuotedPrintableContentTransferEncodingWhenText() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all</b>!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getHeader()
-                .getField("Content-Transfer-Encoding")
-                .getBody())
-            .isEqualTo("quoted-printable");
-    }
-
-    @Test
-    void convertToMimeShouldSetTextBodyWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-        TextBody expected = new BasicBodyFactory().textBody("Hello all!", StandardCharsets.UTF_8);
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("Hello all!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
-    }
-
-    @Test
-    void convertToMimeShouldSetEmptyBodyWhenNoBodyProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-        TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8);
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
-    }
-
-    @Test
-    void convertToMimeShouldSetHtmlBodyWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-        TextBody expected = new BasicBodyFactory().textBody("Hello <b>all</b>!", StandardCharsets.UTF_8);
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all</b>!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
-    }
-
-    @Test
-    void convertToMimeShouldGenerateMultipartWhenHtmlBodyAndTextBodyProvided() throws Exception {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("Hello all!")
-                .htmlBody("Hello <b>all</b>!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isInstanceOf(Multipart.class);
-        assertThat(result.isMultipart()).isTrue();
-        assertThat(result.getMimeType()).isEqualTo("multipart/alternative");
-        Multipart typedResult = (Multipart)result.getBody();
-        assertThat(typedResult.getBodyParts()).hasSize(2);
-    }
-
-    @Test
-    void convertShouldGenerateExpectedMultipartWhenHtmlAndTextBodyProvided() throws Exception {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("Hello all!")
-                .htmlBody("Hello <b>all</b>!")
-                .build();
-
-        String expectedHeaders = "MIME-Version: 1.0\r\n" +
-                "Content-Type: multipart/alternative;\r\n" +
-                " boundary=\"-=Part.";
-        String expectedPart1 = "Content-Type: text/plain; charset=UTF-8\r\n" +
-                "Content-Transfer-Encoding: quoted-printable\r\n" +
-                "\r\n" +
-                "Hello all!\r\n";
-        String expectedPart2 = "Content-Type: text/html; charset=UTF-8\r\n" +
-                "Content-Transfer-Encoding: quoted-printable\r\n" +
-                "\r\n" +
-                "Hello <b>all</b>!\r\n";
-
-        // When
-        byte[] convert = sut.convert(new MessageWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        String actual = new String(convert, StandardCharsets.UTF_8);
-        assertThat(actual).startsWith(expectedHeaders);
-        assertThat(actual).contains(expectedPart1);
-        assertThat(actual).contains(expectedPart2);
-    }
-
-    @Test
-    void convertToMimeShouldSetMimeTypeWhenTextBody() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("Hello all!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getMimeType()).isEqualTo("text/plain");
-    }
-
-    @Test
-    void convertToMimeShouldSetMimeTypeWhenHtmlBody() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getMimeType()).isEqualTo("text/html");
-    }
-
-    @Test
-    void convertToMimeShouldSetEmptyHtmlBodyWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-        TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8);
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
-        assertThat(result.getMimeType()).isEqualTo("text/html");
-    }
-
-    @Test
-    void convertToMimeShouldSetEmptyTextBodyWhenProvided() {
-        // Given
-        MIMEMessageConverter sut = new MIMEMessageConverter();
-        TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8);
-
-        CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("")
-                .build();
-
-        // When
-        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of());
-
-        // Then
-        assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
-        assertThat(result.getMimeType()).isEqualTo("text/plain");
-    }
-
-    @Nested
-    class WithAttachments {
-
-        @Test
-        void convertToMimeShouldAddAttachment() throws Exception {
-            // Given
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String expectedCID = "cid";
-            String expectedMimeType = "image/png";
-            String text = "123456";
-            TextBody expectedBody = new BasicBodyFactory().textBody(text.getBytes(), StandardCharsets.UTF_8);
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid(expectedCID)
-                    .type(expectedMimeType)
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(expectedMimeType)
-                    .build());
-
-            // When
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(1)
-                .extracting(entity -> (Multipart) entity.getBody())
-                .flatExtracting(Multipart::getBodyParts)
-                .anySatisfy(part -> {
-                    assertThat(part.getBody()).isEqualToComparingOnlyGivenFields(expectedBody, "content");
-                    assertThat(part.getDispositionType()).isEqualTo("inline");
-                    assertThat(part.getMimeType()).isEqualTo(expectedMimeType);
-                    assertThat(part.getHeader().getField("Content-ID").getBody()).isEqualTo(expectedCID);
-                    assertThat(part.getContentTransferEncoding()).isEqualTo("base64");
-                });
-        }
-
-        @Test
-        void convertToMimeShouldPreservePartCharset() throws Exception {
-            // Given
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String expectedCID = "cid";
-            String expectedMimeType = "text/calendar; charset=\"iso-8859-1\"";
-            String text = "123456";
-            TextBody expectedBody = new BasicBodyFactory().textBody(text.getBytes(), StandardCharsets.UTF_8);
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid(expectedCID)
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .type(expectedMimeType)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(expectedMimeType)
-                    .build());
-
-            // When
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(1)
-                .extracting(entity -> (Multipart) entity.getBody())
-                .flatExtracting(Multipart::getBodyParts)
-                .anySatisfy(part -> {
-                    assertThat(part.getBody()).isEqualToComparingOnlyGivenFields(expectedBody, "content");
-                    assertThat(part.getDispositionType()).isEqualTo("inline");
-                    assertThat(part.getMimeType()).isEqualTo("text/calendar");
-                    assertThat(part.getCharset()).isEqualTo("iso-8859-1");
-                    assertThat(part.getHeader().getField("Content-ID").getBody()).isEqualTo(expectedCID);
-                    assertThat(part.getContentTransferEncoding()).isEqualTo("base64");
-                });
-        }
-
-        @Test
-        void convertToMimeShouldAddAttachmentAndMultipartAlternativeWhenOneAttachementAndTextAndHtmlBody() throws Exception {
-            // Given
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxId("dead-bada55")
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .textBody("Hello all!")
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-            TextBody expectedTextBody = new BasicBodyFactory().textBody("Hello all!".getBytes(), StandardCharsets.UTF_8);
-            TextBody expectedHtmlBody = new BasicBodyFactory().textBody("Hello <b>all<b>!".getBytes(), StandardCharsets.UTF_8);
-
-            String expectedCID = "cid";
-            String expectedMimeType = "image/png";
-            String text = "123456";
-            TextBody expectedAttachmentBody = new BasicBodyFactory().textBody(text.getBytes(), StandardCharsets.UTF_8);
-
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid(expectedCID)
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .type(expectedMimeType)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(expectedMimeType)
-                    .build());
-
-            // When
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(1)
-                .extracting(entity -> (Multipart) entity.getBody())
-                .flatExtracting(Multipart::getBodyParts)
-                .satisfies(Throwing.consumer(part -> {
-                    assertThat(part.getBody()).isInstanceOf(Multipart.class);
-                    assertThat(part.isMultipart()).isTrue();
-                    assertThat(part.getMimeType()).isEqualTo("multipart/alternative");
-                    assertThat(((Multipart)part.getBody()).getBodyParts()).hasSize(2);
-                    SingleBody textPart = (SingleBody) ((Multipart)part.getBody()).getBodyParts().get(0).getBody();
-                    SingleBody htmlPart = (SingleBody) ((Multipart)part.getBody()).getBodyParts().get(1).getBody();
-                    assertThat(textPart.getInputStream()).hasBinaryContent("Hello all!".getBytes());
-                    assertThat(htmlPart.getInputStream()).hasBinaryContent("Hello <b>all<b>!".getBytes());
-                }), Index.atIndex(0))
-                .satisfies(part -> {
-                    assertThat(part.getBody()).isEqualToComparingOnlyGivenFields(expectedAttachmentBody, "content");
-                    assertThat(part.getDispositionType()).isEqualTo("inline");
-                    assertThat(part.getMimeType()).isEqualTo(expectedMimeType);
-                    assertThat(part.getHeader().getField("Content-ID").getBody()).isEqualTo(expectedCID);
-                }, Index.atIndex(1));
-        }
-
-        @Test
-        void convertShouldEncodeWhenNonASCIICharacters() {
-            // Given
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                    .mailboxId("dead-bada55")
-                    .subject("subject")
-                    .from(DraftEmailer.builder().name("sender").build())
-                    .htmlBody("Some non-ASCII characters: áÄÎßÿ")
-                    .build();
-
-            // When
-            ImmutableList<Attachment.WithBlob> attachments = ImmutableList.of();
-            byte[] convert = sut.convert(new ValueWithId.CreationMessageEntry(
-                    CreationMessageId.of("user|mailbox|1"), testMessage), attachments);
-
-            String expectedEncodedContent = "Some non-ASCII characters: =C3=A1=C3=84=C3=8E=C3=9F=C3=BF";
-
-            // Then
-            String actual = new String(convert, StandardCharsets.US_ASCII);
-            assertThat(actual).contains(expectedEncodedContent);
-        }
-
-        @Test
-        void convertToMimeShouldAddAttachmentAndContainsIndicationAboutTheWayToEncodeFilenamesAttachmentInTheInputStreamWhenSending() throws Exception {
-            // Given
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String expectedCID = "cid";
-            String expectedMimeType = "image/png";
-            String text = "123456";
-            String name = "ديناصور.png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .name(name)
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid(expectedCID)
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .type(expectedMimeType)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(expectedMimeType)
-                    .build());
-
-            // When
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(1)
-                .extracting(entity -> (Multipart) entity.getBody())
-                .flatExtracting(Multipart::getBodyParts)
-                .anySatisfy(part -> assertThat(getNameParameterValue(part)).isEqualTo(name));
-        }
-
-
-        @Test
-        void convertToMimeShouldHaveMixedMultipart() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String text = "123456";
-            String type = "image/png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("ديناصور.png")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-
-            assertThat(result.getBody()).isInstanceOf(Multipart.class);
-            Multipart typedResult = (Multipart)result.getBody();
-            assertThat(typedResult.getSubType()).isEqualTo("mixed");
-        }
-
-        @Test
-        void convertToMimeShouldNotHaveInnerMultipartWhenNoInline() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String text = "123456";
-            String type = "image/png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("ديناصور.png")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .noneMatch(Entity::isMultipart);
-        }
-
-        @Test
-        void convertToMimeShouldHaveChildrenAttachmentParts() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String text = "123456";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            String type = "image/png";
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("ديناصور.png")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .extracting(Entity::getDispositionType)
-                .anySatisfy(contentDisposition -> assertThat(contentDisposition).isEqualTo("attachment"));
-        }
-
-        @Test
-        void convertToMimeShouldNotThrowWhenNameInContentTypeFieldAndAttachmentMetadata() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String text = "123456";
-            String type = "image/png; name=abc.png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("fgh.png")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            assertThatCode(() -> sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                    CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment)))
-                .doesNotThrowAnyException();
-        }
-
-        @Test
-        void attachmentNameShouldBeOverriddenWhenSpecified() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            String text = "123456";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            String type = "image/png; name=abc.png; charset=\"iso-8859-1\"";
-
-            Attachment attachment = Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("fgh.png")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build();
-
-            assertThat(sut.contentTypeField(attachment).getBody())
-                .isEqualTo("image/png; charset=iso-8859-1; name=\"=?US-ASCII?Q?fgh.png?=\"");
-        }
-
-        @Test
-        void nameShouldBeAddedToContentTypeWhenSpecified() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            String text = "123456";
-            String type = "image/png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            Attachment attachment = Attachment.builder()
-                .blobId(BlobId.of(blodId.getId()))
-                .cid("cid")
-                .name("fgh.png")
-                .isInline(false)
-                .size(text.getBytes().length)
-                .type(type)
-                .build();
-
-            assertThat(sut.contentTypeField(attachment).getBody())
-                .isEqualTo("image/png; name=\"=?US-ASCII?Q?fgh.png?=\"");
-        }
-
-        @Test
-        void attachmentNameShouldBePreservedWhenNameNotSpecified() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            String text = "123456";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            String type = "image/png; name=abc.png";
-
-            Attachment attachment = Attachment.builder()
-                .blobId(BlobId.of(blodId.getId()))
-                .cid("cid")
-                .isInline(false)
-                .size(text.getBytes().length)
-                .type(type)
-                .build();
-
-            assertThat(sut.contentTypeField(attachment).getBody())
-                .isEqualTo(type);
-        }
-
-        @Test
-        void attachmentNameShouldBeUnspecifiedWhenNone() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            String text = "123456";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            String type = "image/png";
-            Attachment attachment = Attachment.builder()
-                .blobId(BlobId.of(blodId.getId()))
-                .cid("cid")
-                .isInline(false)
-                .size(text.getBytes().length)
-                .type(type)
-                .build();
-
-            assertThat(sut.contentTypeField(attachment).getBody())
-                .isEqualTo(type);
-        }
-
-        @Test
-        void convertToMimeShouldHaveChildMultipartWhenOnlyInline() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String name = "ديناصور.png";
-            String text = "123456";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-            String type = "image/png";
-
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name(name)
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                    CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(1)
-                .allMatch(Entity::isMultipart)
-                .extracting(entity -> (Multipart) entity.getBody())
-                .extracting(Multipart::getSubType)
-                .allSatisfy(subType -> assertThat(subType).isEqualTo("related"));
-        }
-
-        @Test
-        void convertToMimeShouldHaveChildMultipartWhenBothInlinesAndAttachments() throws Exception {
-            MIMEMessageConverter sut = new MIMEMessageConverter();
-
-            CreationMessage testMessage = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("dead-bada55"))
-                .subject("subject")
-                .from(DraftEmailer.builder().name("sender").build())
-                .htmlBody("Hello <b>all<b>!")
-                .build();
-
-            String text = "inline data";
-            String type = "image/png";
-            StringBackedAttachmentId blodId = StringBackedAttachmentId.from("blodId");
-
-            Attachment.WithBlob inline = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid")
-                    .name("ديناصور.png")
-                    .isInline(true)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            String text2 = "attachment data";
-            final StringBackedAttachmentId blodId2 = StringBackedAttachmentId.from("blodId2");
-            Attachment.WithBlob attachment = new Attachment.WithBlob(
-                Attachment.builder()
-                    .blobId(BlobId.of(blodId.getId()))
-                    .cid("cid2")
-                    .name("att.pdf")
-                    .isInline(false)
-                    .size(text.getBytes().length)
-                    .type(type)
-                    .build(),
-                Blob.builder()
-                    .payload(() -> new ByteArrayInputStream(text.getBytes()))
-                    .id(BlobId.of(blodId.getId()))
-                    .size(text.getBytes().length)
-                    .contentType(type)
-                    .build());
-
-            Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
-                    CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(inline, attachment));
-            Multipart typedResult = (Multipart)result.getBody();
-
-            assertThat(typedResult.getBodyParts())
-                .hasSize(2)
-                .satisfies(part -> {
-                    Multipart multipartRelated = (Multipart) part.getBody();
-                    assertThat(multipartRelated.getSubType()).isEqualTo("related");
-                    assertThat(multipartRelated.getBodyParts())
-                        .extracting(Entity::getDispositionType)
-                        .contains("inline");
-                }, Index.atIndex(0))
-                .satisfies(part -> {
-                    assertThat(part.getDispositionType()).isEqualTo("attachment");
-                }, Index.atIndex(1));
-        }
-
-        private String getNameParameterValue(Entity attachmentPart) {
-            return ((ContentTypeField) attachmentPart.getHeader().getField("Content-Type")).getParameter("name");
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MessageSenderTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MessageSenderTest.java
deleted file mode 100644
index 0ebed77..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/MessageSenderTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-
-import java.nio.charset.StandardCharsets;
-import java.time.Instant;
-import java.util.Collection;
-
-import jakarta.mail.util.SharedByteArrayInputStream;
-
-import org.apache.james.core.MailAddress;
-import org.apache.james.core.MaybeSender;
-import org.apache.james.jmap.draft.model.EnvelopeUtils;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory.MetaDataWithContent;
-import org.apache.james.jmap.memory.projections.MemoryMessageFastViewProjection;
-import org.apache.james.jmap.utils.JsoupHtmlTextExtractor;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.server.core.Envelope;
-import org.apache.james.util.html.HtmlTextExtractor;
-import org.apache.james.util.mime.MessageContentExtractor;
-import org.apache.mailet.Mail;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-class MessageSenderTest {
-
-    private Envelope envelope;
-    private MetaDataWithContent message;
-    private MessageFullView jmapMessage;
-
-    @BeforeEach
-    void setup() throws Exception {
-        String headers = "From: me@example.com\n"
-            + "To: 1@example.com\n"
-            + "Cc: 2@example.com, 3@example.com\n"
-            + "Bcc: 4@example.com\n"
-            + "Subject: news\n";
-        String content = headers
-            + "Hello! How are you?";
-
-        message = MetaDataWithContent.builder()
-            .uid(MessageUid.of(2))
-            .keywords(Keywords.strictFactory().from(Keyword.SEEN))
-            .size(content.length())
-            .internalDate(Instant.now())
-            .sharedContent(new SharedByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))
-            .attachments(ImmutableList.of())
-            .mailboxId(InMemoryId.of(3))
-            .messageId(TestMessageId.of(2))
-            .build();
-
-        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
-        HtmlTextExtractor htmlTextExtractor = new JsoupHtmlTextExtractor();
-
-        BlobManager blobManager = mock(BlobManager.class);
-        MessageIdManager messageIdManager = mock(MessageIdManager.class);
-        MessageFullViewFactory messageFullViewFactory = new MessageFullViewFactory(blobManager, messageContentExtractor, htmlTextExtractor, messageIdManager,
-            new MemoryMessageFastViewProjection(new RecordingMetricFactory()));
-        jmapMessage = messageFullViewFactory.fromMetaDataWithContent(message).block();
-        envelope = EnvelopeUtils.fromMessage(jmapMessage);
-    }
-
-    @Test
-    void buildMailShouldThrowWhenNullMailboxMessage() throws Exception {
-        MetaDataWithContent message = null;
-        assertThatThrownBy(() -> MessageSender.buildMail(message, envelope)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    void buildMailShouldThrowWhenNullJmapMessage() throws Exception {
-        Envelope envelope = null;
-        assertThatThrownBy(() -> MessageSender.buildMail(message, envelope)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    void buildMailShouldGenerateMailWithExpectedProperties() throws Exception {
-        String expectedName = jmapMessage.getId().serialize();
-        MailAddress expectedSender = new MailAddress("me@example.com");
-        Collection<MailAddress> expectedRecipients = ImmutableSet.of(
-            new MailAddress("1@example.com"),
-            new MailAddress("3@example.com"),
-            new MailAddress("2@example.com"),
-            new MailAddress("4@example.com"));
-
-        Mail actual = MessageSender.buildMail(message, envelope);
-
-        assertThat(actual.getName()).isEqualTo(expectedName);
-        assertThat(actual.getMaybeSender()).isEqualTo(MaybeSender.of(expectedSender));
-        assertThat(actual.getRecipients()).containsAll(expectedRecipients);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/RequestHandlerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/RequestHandlerTest.java
deleted file mode 100644
index 8e6c071..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/RequestHandlerTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-
-import java.util.List;
-
-import jakarta.inject.Inject;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.model.AuthenticatedRequest;
-import org.apache.james.jmap.draft.model.InvocationRequest;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.methods.JmapResponseWriter;
-import org.apache.james.jmap.methods.JmapResponseWriterImpl;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-
-import reactor.core.publisher.Flux;
-
-public class RequestHandlerTest {
-
-    public static class TestJmapRequest implements JmapRequest {
-
-        public String id;
-        public String name;
-
-        public String getId() {
-            return id;
-        }
-
-        public String getName() {
-            return name;
-        }
-    }
-
-    public static class TestJmapResponse implements Method.Response {
-
-        private final String id;
-        private final String name;
-        private final String message;
-
-        public TestJmapResponse(String id, String name, String message) {
-            this.id = id;
-            this.name = name;
-            this.message = message;
-        }
-
-        public String getId() {
-            return id;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getMessage() {
-            return message;
-        }
-    }
-
-    public static class TestMethod implements Method {
-
-        @Inject
-        @VisibleForTesting TestMethod() {
-        }
-
-        @Override
-        public Method.Request.Name requestHandled() {
-            return Method.Request.name("getTestMethod");
-        }
-
-        @Override
-        public Class<? extends JmapRequest> requestType() {
-            return TestJmapRequest.class;
-        }
-
-        @Override
-        public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-            Preconditions.checkArgument(request instanceof TestJmapRequest);
-            TestJmapRequest typedRequest = (TestJmapRequest) request;
-            return Flux.just(
-                    JmapResponse.builder()
-                            .response(new TestJmapResponse(typedRequest.getId(), typedRequest.getName(), "works"))
-                            .responseName(Response.name("test"))
-                            .methodCallId(MethodCallId.of("#0"))
-                            .build());
-        }
-    }
-
-    private RequestHandler testee;
-    private JmapRequestParser jmapRequestParser;
-    private JmapResponseWriter jmapResponseWriter;
-    private MailboxSession session;
-
-    @Before
-    public void setup() {
-        ObjectMapperFactory objectMapperFactory = new ObjectMapperFactory(new InMemoryId.Factory(), new InMemoryMessageId.Factory());
-        jmapRequestParser = new JmapRequestParserImpl(objectMapperFactory);
-        jmapResponseWriter = new JmapResponseWriterImpl(objectMapperFactory);
-        session = MailboxSessionUtil.create(Username.of("bob"));
-        testee = new RequestHandler(ImmutableSet.of(new TestMethod()), jmapRequestParser, jmapResponseWriter);
-    }
-
-
-    @Test(expected = IllegalStateException.class)
-    public void processShouldThrowWhenUnknownMethod() throws Exception {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("unknwonMethod"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{\"id\": \"id\"}"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        RequestHandler requestHandler = new RequestHandler(ImmutableSet.of(), jmapRequestParser, jmapResponseWriter);
-        requestHandler.handle(AuthenticatedRequest.decorate(InvocationRequest.deserialize(nodes), session));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void requestHandlerShouldThrowWhenAMethodIsRecordedTwice() {
-        new RequestHandler(
-                ImmutableSet.of(
-                        new TestMethod(),
-                        new TestMethod()),
-                jmapRequestParser, 
-                jmapResponseWriter);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void requestHandlerShouldThrowWhenTwoMethodsWithSameName() {
-        new RequestHandler(
-                ImmutableSet.of(
-                        new NamedMethod(Method.Request.name("name")),
-                        new NamedMethod(Method.Request.name("name"))),
-                jmapRequestParser, 
-                jmapResponseWriter);
-    }
-
-    @Test
-    public void requestHandlerMayBeCreatedWhenTwoMethodsWithDifferentName() {
-        new RequestHandler(
-                ImmutableSet.of(
-                        new NamedMethod(Method.Request.name("name")), 
-                        new NamedMethod(Method.Request.name("name2"))),
-                jmapRequestParser, 
-                jmapResponseWriter);
-    }
-
-    private class NamedMethod implements Method {
-
-        private final Method.Request.Name methodName;
-
-        public NamedMethod(Method.Request.Name methodName) {
-            this.methodName = methodName;
-            
-        }
-
-        @Override
-        public Method.Request.Name requestHandled() {
-            return methodName;
-        }
-        
-        @Override
-        public Class<? extends JmapRequest> requestType() {
-            return null;
-        }
-        
-        @Override
-        public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
-            return Flux.empty();
-        }
-    }
-
-    @Test
-    public void processShouldWorkWhenKnownMethod() throws Exception {
-        ObjectNode parameters = new ObjectNode(new JsonNodeFactory(false));
-        parameters.put("id", "testId");
-        parameters.put("name", "testName");
-        
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getTestMethod"),
-                parameters,
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        List<InvocationResponse> responses = testee.handle(AuthenticatedRequest.decorate(InvocationRequest.deserialize(nodes), session))
-                .collectList()
-                .block();
-
-        assertThat(responses).hasSize(1)
-                .extracting(
-                        x -> x.getResults().findValue("id").asText(),
-                        x -> x.getResults().findValue("name").asText(),
-                        x -> x.getResults().findValue("message").asText())
-                .containsExactly(tuple("testId", "testName", "works"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessorTest.java
deleted file mode 100644
index 6dbcd7c..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesCreationProcessorTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.apache.james.jmap.draft.model.MailboxCreationId;
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.mailbox.MailboxCreateRequest;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.SubscriptionManager;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryId.Factory;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SetMailboxesCreationProcessorTest {
-
-    private Factory mailboxIdFactory;
-    private MailboxFactory mailboxFactory;
-    private SetMailboxesCreationProcessor sut;
-    private MailboxManager mailboxManager;
-
-    @Before
-    public void setup() {
-        mailboxManager = mock(MailboxManager.class);
-        mailboxIdFactory = new InMemoryId.Factory();
-        sut = new SetMailboxesCreationProcessor(mailboxManager, mailboxFactory, mailboxIdFactory, new RecordingMetricFactory());
-    }
-
-    @Test
-    public void processShouldReturnNotCreatedWhenMailboxExceptionOccured() throws Exception {
-        MailboxCreationId parentId = MailboxCreationId.of("0");
-        MailboxId parentMailboxId = mailboxIdFactory.fromString(parentId.getCreationId());
-        MailboxCreationId mailboxCreationId = MailboxCreationId.of("1");
-        SetMailboxesRequest request = SetMailboxesRequest.builder()
-                .create(mailboxCreationId, MailboxCreateRequest.builder().name("name").parentId(parentId).build())
-                .build();
-
-        MailboxSession mailboxSession = mock(MailboxSession.class);
-        when(mailboxManager.getMailbox(parentMailboxId, mailboxSession))
-            .thenThrow(new MailboxException());
-
-        SetMailboxesResponse setMailboxesResponse = sut.process(request, mailboxSession);
-        assertThat(setMailboxesResponse.getCreated()).isEmpty();
-        assertThat(setMailboxesResponse.getNotCreated()).containsEntry(mailboxCreationId, 
-                SetError.builder()
-                    .type(SetError.Type.ERROR)
-                    .description("An error occurred when creating the mailbox '1'")
-                    .build());
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesMethodTest.java
deleted file mode 100644
index 8b3aa56..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesMethodTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.stream.Stream;
-
-import org.apache.james.jmap.draft.model.GetMailboxesRequest;
-import org.apache.james.jmap.draft.model.MailboxCreationId;
-import org.apache.james.jmap.methods.JmapRequest;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxCreateRequest;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import reactor.core.publisher.Mono;
-
-public class SetMailboxesMethodTest {
-
-    private static final ImmutableSet<SetMailboxesProcessor> NO_PROCESSOR = ImmutableSet.of();
-    private static final DefaultMetricFactory TIME_METRIC_FACTORY = new DefaultMetricFactory();
-
-    @Test
-    public void requestHandledShouldBeSetMailboxes() {
-        assertThat(new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).requestHandled().getName()).isEqualTo("setMailboxes");
-    }
-
-    @Test
-    public void requestTypeShouldBeSetMailboxes() {
-        assertThat(new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).requestType()).isEqualTo(SetMailboxesRequest.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenNullJmapRequest() {
-        MailboxSession session = mock(MailboxSession.class);
-        JmapRequest nullJmapRequest = null;
-        assertThatThrownBy(() -> new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).processToStream(nullJmapRequest, MethodCallId.of("methodCallId"), session))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenNullMethodCallId() {
-        MailboxSession session = mock(MailboxSession.class);
-        JmapRequest jmapRequest = mock(JmapRequest.class);
-        MethodCallId nullMethodCallId = null;
-        assertThatThrownBy(() -> new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).processToStream(jmapRequest, nullMethodCallId, session))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenNullMailboxSession() {
-        MailboxSession nullMailboxSession = null;
-        JmapRequest jmapRequest = mock(JmapRequest.class);
-        assertThatThrownBy(() -> new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).processToStream(jmapRequest, MethodCallId.of("methodCallId"), nullMailboxSession))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void processShouldThrowWhenJmapRequestTypeMismatch() {
-        MailboxSession session = mock(MailboxSession.class);
-        JmapRequest getMailboxesRequest = GetMailboxesRequest.builder().build();
-        assertThatThrownBy(() -> new SetMailboxesMethod(NO_PROCESSOR, TIME_METRIC_FACTORY).processToStream(getMailboxesRequest, MethodCallId.of("methodCallId"), session))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void processShouldCallCreatorProcessorWhenCreationRequest() {
-        MailboxCreationId creationId = MailboxCreationId.of("create-id01");
-        MailboxCreateRequest fooFolder = MailboxCreateRequest.builder().name("fooFolder").build();
-        SetMailboxesRequest creationRequest = SetMailboxesRequest.builder().create(creationId, fooFolder).build();
-
-        Mailbox createdfooFolder = Mailbox.builder().name("fooFolder").id(InMemoryId.of(123)).build();
-        SetMailboxesResponse creationResponse = SetMailboxesResponse.builder().created(creationId, createdfooFolder).build();
-        JmapResponse jmapResponse = JmapResponse.builder()
-            .response(creationResponse)
-            .methodCallId(MethodCallId.of("methodCallId"))
-            .responseName(SetMailboxesMethod.RESPONSE_NAME)
-            .build();
-
-        MailboxSession session = mock(MailboxSession.class);
-        SetMailboxesProcessor creatorProcessor = mock(SetMailboxesProcessor.class);
-        when(creatorProcessor.processReactive(creationRequest, session)).thenReturn(Mono.just(creationResponse));
-
-        Stream<JmapResponse> actual =
-            new SetMailboxesMethod(ImmutableSet.of(creatorProcessor), TIME_METRIC_FACTORY)
-                    .process(creationRequest, MethodCallId.of("methodCallId"), session)
-            .toStream();
-
-        assertThat(actual).contains(jmapResponse);
-    }
-
-    @Test
-    public void processShouldCallDestructorProcessorWhenCreationRequest() {
-        ImmutableList<MailboxId> deletions = ImmutableList.of(InMemoryId.of(1));
-        SetMailboxesRequest destructionRequest = SetMailboxesRequest.builder().destroy(deletions).build();
-
-        SetMailboxesResponse destructionResponse = SetMailboxesResponse.builder().destroyed(deletions).build();
-        JmapResponse jmapResponse = JmapResponse.builder()
-            .response(destructionResponse)
-            .methodCallId(MethodCallId.of("methodCallId"))
-            .responseName(SetMailboxesMethod.RESPONSE_NAME)
-            .build();
-
-        MailboxSession session = mock(MailboxSession.class);
-        SetMailboxesProcessor destructorProcessor = mock(SetMailboxesProcessor.class);
-        when(destructorProcessor.processReactive(destructionRequest, session)).thenReturn(Mono.just(destructionResponse));
-
-        Stream<JmapResponse> actual =
-            new SetMailboxesMethod(ImmutableSet.of(destructorProcessor), TIME_METRIC_FACTORY)
-                    .process(destructionRequest, MethodCallId.of("methodCallId"), session)
-            .toStream();
-
-        assertThat(actual).contains(jmapResponse);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessorTest.java
deleted file mode 100644
index 7faa903..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMailboxesUpdateProcessorTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.model.MailboxFactory;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetMailboxesResponse;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxUpdateRequest;
-import org.apache.james.jmap.draft.utils.MailboxUtils;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import reactor.core.publisher.Mono;
-
-public class SetMailboxesUpdateProcessorTest {
-
-    private MailboxManager mockedMailboxManager;
-    private MailboxUtils mockedMailboxUtils;
-    private MailboxFactory mockedMailboxFactory;
-    private MailboxSession mockedMailboxSession;
-    private SetMailboxesUpdateProcessor sut;
-
-    @Before
-    public void setup() {
-        mockedMailboxManager = mock(MailboxManager.class);
-        mockedMailboxUtils = mock(MailboxUtils.class);
-        mockedMailboxFactory = mock(MailboxFactory.class);
-        mockedMailboxSession = mock(MailboxSession.class);
-        MetricFactory metricFactory = new RecordingMetricFactory();
-        sut = new SetMailboxesUpdateProcessor(mockedMailboxUtils, mockedMailboxManager, mockedMailboxFactory, metricFactory);
-    }
-
-    @Test
-    public void processShouldReturnNotUpdatedWhenMailboxExceptionOccured() throws Exception {
-        // Given
-        InMemoryId mailboxId = InMemoryId.of(1);
-        InMemoryId newParentId = InMemoryId.of(2);
-        SetMailboxesRequest request = SetMailboxesRequest.builder()
-                .update(mailboxId, MailboxUpdateRequest.builder().parentId(newParentId).build())
-                .build();
-        Mailbox mailbox = Mailbox.builder().id(mailboxId).name("name").role(Optional.empty()).build();
-
-        MailboxFactory.MailboxBuilder mockBuilder = mock(MailboxFactory.MailboxBuilder.class);
-        when(mockBuilder.id(mailboxId))
-            .thenReturn(mockBuilder);
-        when(mockBuilder.session(mockedMailboxSession))
-            .thenReturn(mockBuilder);
-        when(mockBuilder.build())
-            .thenReturn(Mono.just(mailbox));
-
-        when(mockedMailboxFactory.builder())
-            .thenReturn(mockBuilder);
-        when(mockedMailboxManager.getMailbox(newParentId, mockedMailboxSession))
-            .thenReturn(mock(MessageManager.class));
-        when(mockedMailboxUtils.hasChildren(mailboxId, mockedMailboxSession))
-            .thenThrow(new MailboxException());
-
-        // When
-        SetMailboxesResponse setMailboxesResponse = sut.process(request, mockedMailboxSession);
-
-        // Then
-        verify(mockBuilder, times(1)).id(Mockito.eq(mailboxId));
-        verify(mockBuilder, times(1)).session(Mockito.eq(mockedMailboxSession));
-        assertThat(setMailboxesResponse.getUpdated()).isEmpty();
-        assertThat(setMailboxesResponse.getNotUpdated()).containsEntry(mailboxId, SetError.builder().type(SetError.Type.ERROR).description("An error occurred when updating the mailbox").build());
-    }
-
-    @Test
-    public void requestChangedShouldReturnFalseWhenRequestValueAndStoreValueAreEmpty() throws Exception {
-        assertThat(sut.requestChanged(Optional.<String>empty(), Optional.empty())).isFalse();
-    }
-
-    @Test
-    public void requestChangedShouldReturnFalseWhenEmptyRequestMeansNoChanging() throws Exception {
-        assertThat(sut.requestChanged(Optional.empty(), Optional.of("any"))).isFalse();
-    }
-
-    @Test
-    public void requestChangedShouldReturnTrueWhenEmptyStoreValue() throws Exception {
-        assertThat(sut.requestChanged(Optional.of("any"), Optional.empty())).isTrue();
-    }
-
-    @Test
-    public void requestChangedShouldReturnTrueWhenRequestValueAndStoreValueAreNotTheSame() throws Exception {
-        assertThat(sut.requestChanged(Optional.of("any"), Optional.of("other"))).isTrue();
-    }
-
-    @Test
-    public void requestChangedShouldReturnFalseWhenRequestValueAndStoreValueAreTheSame() throws Exception {
-        assertThat(sut.requestChanged(Optional.of("any"), Optional.of("any"))).isFalse();
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
deleted file mode 100644
index 7b18c27..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.net.UnknownHostException;
-import java.util.Collection;
-import java.util.Optional;
-import java.util.function.Supplier;
-
-import org.apache.commons.configuration2.ex.ConfigurationException;
-import org.apache.james.UserEntityValidator;
-import org.apache.james.core.Domain;
-import org.apache.james.core.Username;
-import org.apache.james.dnsservice.api.DNSService;
-import org.apache.james.domainlist.api.DomainListException;
-import org.apache.james.domainlist.lib.DomainListConfiguration;
-import org.apache.james.domainlist.memory.MemoryDomainList;
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.jmap.draft.exceptions.MailboxNotOwnedException;
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessage.DraftEmailer;
-import org.apache.james.jmap.draft.model.CreationMessageId;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.model.message.view.MessageFullViewFactory;
-import org.apache.james.jmap.send.MailMetadata;
-import org.apache.james.jmap.draft.send.MailSpool;
-import org.apache.james.jmap.memory.projections.MemoryMessageFastViewProjection;
-import org.apache.james.jmap.utils.JsoupHtmlTextExtractor;
-import org.apache.james.mailbox.AttachmentManager;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SystemMailboxesProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.exception.MailboxNotFoundException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxId.Factory;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.mailbox.model.ThreadId;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.rrt.api.AliasReverseResolver;
-import org.apache.james.rrt.api.CanSendFrom;
-import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
-import org.apache.james.rrt.api.RecipientRewriteTableException;
-import org.apache.james.rrt.lib.AliasReverseResolverImpl;
-import org.apache.james.rrt.lib.CanSendFromImpl;
-import org.apache.james.rrt.lib.Mapping;
-import org.apache.james.rrt.lib.MappingSource;
-import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
-import org.apache.james.user.memory.MemoryUsersRepository;
-import org.apache.james.util.html.HtmlTextExtractor;
-import org.apache.james.util.mime.MessageContentExtractor;
-import org.apache.mailet.Mail;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-public class SetMessagesCreationProcessorTest {
-    private static final Username USER = Username.of("user@example.com");
-    private static final Username OTHER_USER = Username.of("other@example.com");
-    private static final String OUTBOX = "outbox";
-    private static final InMemoryId OUTBOX_ID = InMemoryId.of(12345);
-    private static final String DRAFTS = "drafts";
-    private static final InMemoryId DRAFTS_ID = InMemoryId.of(12);
-    private static final Long TEST_MESSAGE_SIZE = 1L;
-
-    private final CreationMessage.Builder creationMessageBuilder = CreationMessage.builder()
-            .from(DraftEmailer.builder().name("alice").email("alice@example.com").build())
-            .to(ImmutableList.of(DraftEmailer.builder().name("bob").email("bob@example.com").build()))
-            .subject("Hey! ");
-
-    private final CreationMessageId creationMessageId = CreationMessageId.of("dlkja");
-    
-    private final SetMessagesRequest createMessageInOutbox = SetMessagesRequest.builder()
-            .create(
-                    creationMessageId, 
-                    creationMessageBuilder
-                        .mailboxId(OUTBOX_ID.serialize())
-                        .from(DraftEmailer.builder().name("user").email("user@example.com").build())
-                        .build())
-            .build();
-
-    private MessageFullViewFactory messageFullViewFactory;
-    private MailSpool mockedMailSpool;
-    private SystemMailboxesProvider fakeSystemMailboxesProvider;
-    private MailboxSession session;
-    private AttachmentManager mockedAttachmentManager;
-    private MailboxManager mockedMailboxManager;
-    private Factory mockedMailboxIdFactory;
-    private MemoryRecipientRewriteTable recipientRewriteTable;
-    private CanSendFrom canSendFrom;
-    private SetMessagesCreationProcessor sut;
-    private MessageManager outbox;
-    private MessageManager drafts;
-    private Optional<MessageManager> optionalOutbox;
-    private Optional<MessageManager> optionalDrafts;
-
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-    private MessageAppender messageAppender;
-    private MessageSender messageSender;
-    private ReferenceUpdater referenceUpdater;
-
-    @Before
-    public void setUp() throws MailboxException, DomainListException, UnknownHostException, ConfigurationException {
-        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
-        HtmlTextExtractor htmlTextExtractor = new JsoupHtmlTextExtractor();
-        BlobManager blobManager = mock(BlobManager.class);
-        when(blobManager.retrieve(any(Collection.class), any())).thenReturn(Flux.empty());
-        MessageIdManager messageIdManager = mock(MessageIdManager.class);
-        recipientRewriteTable = new MemoryRecipientRewriteTable();
-
-        DNSService dnsService = mock(DNSService.class);
-        MemoryDomainList domainList = new MemoryDomainList(dnsService);
-        domainList.configure(DomainListConfiguration.DEFAULT);
-        domainList.addDomain(Domain.of("example.com"));
-        domainList.addDomain(Domain.of("other.org"));
-        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
-        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
-        recipientRewriteTable.setDomainList(domainList);
-        recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
-        AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
-        canSendFrom = new CanSendFromImpl(recipientRewriteTable, aliasReverseResolver);
-        messageFullViewFactory = new MessageFullViewFactory(blobManager, messageContentExtractor, htmlTextExtractor,
-            messageIdManager,
-            new MemoryMessageFastViewProjection(new RecordingMetricFactory()));
-        mockedMailSpool = mock(MailSpool.class);
-        when(mockedMailSpool.send(any(), any())).thenReturn(Mono.empty());
-        mockedAttachmentManager = mock(AttachmentManager.class);
-        when(mockedAttachmentManager.getAttachments(any(), any())).thenReturn(ImmutableList.of());
-        mockedMailboxManager = mock(MailboxManager.class);
-        mockedMailboxIdFactory = mock(Factory.class);
-        MessageIdManager mockMessageIdManager = mock(MessageIdManager.class);
-        
-        fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> optionalOutbox, () -> optionalDrafts);
-        session = MailboxSessionUtil.create(USER);
-        MIMEMessageConverter mimeMessageConverter = new MIMEMessageConverter();
-        messageAppender = new MessageAppender(mockedMailboxManager, mockMessageIdManager, mimeMessageConverter, blobManager, JMAPConfiguration.builder().enable().build());
-        messageSender = new MessageSender(mockedMailSpool);
-        referenceUpdater = new ReferenceUpdater(mockMessageIdManager, mockedMailboxManager);
-        sut = new SetMessagesCreationProcessor(messageFullViewFactory,
-            fakeSystemMailboxesProvider,
-            new RecordingMetricFactory(),
-            mockedMailboxManager,
-            mockedMailboxIdFactory,
-            messageAppender,
-            messageSender,
-            referenceUpdater,
-            canSendFrom);
-        
-        outbox = mock(MessageManager.class);
-        when(mockedMailboxIdFactory.fromString(OUTBOX_ID.serialize()))
-            .thenReturn(OUTBOX_ID);
-        when(mockedMailboxManager.getMailbox(OUTBOX_ID, session))
-            .thenReturn(outbox);
-        when(mockedMailboxManager.getMailboxReactive(OUTBOX_ID, session))
-            .thenReturn(Mono.just(outbox));
-        
-        when(outbox.getId()).thenReturn(OUTBOX_ID);
-        when(outbox.getMailboxPath()).thenReturn(MailboxPath.forUser(USER, OUTBOX));
-        
-        when(outbox.appendMessageReactive(any(MessageManager.AppendCommand.class), any(MailboxSession.class)))
-            .thenReturn(Mono.just(new MessageManager.AppendResult(new ComposedMessageId(OUTBOX_ID, TestMessageId.of(23), MessageUid.of(1)), TEST_MESSAGE_SIZE,
-                Optional.of(ImmutableList.of()), ThreadId.fromBaseMessageId(TestMessageId.of(23)))));
-
-        drafts = mock(MessageManager.class);
-        when(drafts.getId()).thenReturn(DRAFTS_ID);
-        when(drafts.getMailboxPath()).thenReturn(MailboxPath.forUser(USER, DRAFTS));
-        optionalOutbox = Optional.of(outbox);
-        optionalDrafts = Optional.of(drafts);
-    }
-
-    @Test
-    public void processShouldReturnEmptyCreatedWhenRequestHasEmptyCreate() {
-        SetMessagesRequest requestWithEmptyCreate = SetMessagesRequest.builder().build();
-
-        SetMessagesResponse result = sut.process(requestWithEmptyCreate, session);
-
-        assertThat(result.getCreated()).isEmpty();
-        assertThat(result.getNotCreated()).isEmpty();
-    }
-
-    @Test
-    public void processShouldThrowWhenBothIsFlagAndKeywords() {
-        expectedException.expect(IllegalArgumentException.class);
-        SetMessagesRequest createMessageWithError = SetMessagesRequest.builder()
-            .create(
-                creationMessageId,
-                creationMessageBuilder
-                    .mailboxId(OUTBOX_ID.serialize())
-                    .isAnswered(Optional.of(true))
-                    .keywords(ImmutableMap.of("$Answered", true))
-                    .build())
-            .build();
-
-        sut.process(createMessageWithError, session);
-    }
-
-    @Test
-    public void processShouldCreateWhenKeywords() {
-        SetMessagesRequest createMessageWithKeywords = SetMessagesRequest.builder()
-            .create(
-                creationMessageId,
-                creationMessageBuilder
-                    .mailboxId(OUTBOX_ID.serialize())
-                    .keywords(ImmutableMap.of("$Answered", true))
-                    .build())
-            .build();
-
-        SetMessagesResponse result = sut.process(createMessageWithKeywords, session);
-
-        assertThat(result.getCreated()).isNotEmpty();
-        assertThat(result.getNotCreated()).isEmpty();
-    }
-
-    @Test
-    public void processShouldCreateWhenIsFlag() {
-        SetMessagesRequest createMessageWithKeywords = SetMessagesRequest.builder()
-            .create(
-                creationMessageId,
-                creationMessageBuilder
-                    .mailboxId(OUTBOX_ID.serialize())
-                    .isAnswered(Optional.of(true))
-                    .build())
-            .build();
-
-        SetMessagesResponse result = sut.process(createMessageWithKeywords, session);
-
-        assertThat(result.getCreated()).isNotEmpty();
-        assertThat(result.getNotCreated()).isEmpty();
-    }
-
-    @Test
-    public void processShouldReturnNonEmptyCreatedWhenRequestHasNonEmptyCreate() throws MailboxException {
-        // Given
-        sut = new SetMessagesCreationProcessor(messageFullViewFactory, fakeSystemMailboxesProvider, new RecordingMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory, messageAppender, messageSender, referenceUpdater, canSendFrom);
-
-        // When
-        SetMessagesResponse result = sut.process(createMessageInOutbox, session);
-
-        // Then
-        assertThat(result.getCreated()).isNotEmpty();
-        assertThat(result.getNotCreated()).isEmpty();
-    }
-
-    @Ignore("JAMES-1716 : should report an error")
-    @Test
-    public void processShouldReturnErrorWhenOutboxNotFound() {
-        // Given
-        TestSystemMailboxesProvider doNotProvideOutbox = new TestSystemMailboxesProvider(Optional::empty, () -> optionalDrafts);
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(messageFullViewFactory, doNotProvideOutbox,
-            new RecordingMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory,
-            messageAppender,
-            messageSender,
-            referenceUpdater,
-            canSendFrom);
-        // When
-        SetMessagesResponse actual = sut.process(createMessageInOutbox, session);
-        
-        assertThat(actual.getNotCreated()).hasSize(1).containsKey(creationMessageId);
-        assertThat(actual.getNotCreated().get(creationMessageId).getType()).isEqualTo("invalidProperties");
-        assertThat(actual.getNotCreated().get(creationMessageId).getDescription()).contains("target mailbox does not exists");
-    }
-
-    @Test
-    public void processShouldCallAppendMessageWhenRequestHasNonEmptyCreate() throws MailboxException {
-        // When
-        sut.process(createMessageInOutbox, session);
-
-        // Then
-        verify(outbox).appendMessageReactive(any(MessageManager.AppendCommand.class), any(MailboxSession.class));
-    }
-
-    @Test
-    public void processShouldSendMailWhenRequestHasNonEmptyCreate() throws Exception {
-        // When
-        sut.process(createMessageInOutbox, session);
-
-        // Then
-        verify(mockedMailSpool).send(any(Mail.class), any(MailMetadata.class));
-    }
-
-    @Test
-    public void processShouldNotSpoolMailWhenNotSavingToOutbox() throws Exception {
-        // When
-        SetMessagesRequest notInOutboxCreationRequest =
-                SetMessagesRequest.builder()
-                    .create(CreationMessageId.of("anything-really"),
-                            creationMessageBuilder.mailboxId("any-id-but-outbox-id")
-                        .build())
-                    .build();
-        when(mockedMailboxManager.getMailbox(any(MailboxId.class), any()))
-            .thenReturn(outbox);
-        when(mockedMailboxIdFactory.fromString(anyString())).thenReturn(OUTBOX_ID);
-
-        sut.process(notInOutboxCreationRequest, session);
-
-        // Then
-        verify(mockedMailSpool, never()).send(any(Mail.class), any(MailMetadata.class));
-    }
-
-    @Test
-    public void processShouldNotSendWhenSavingToDrafts() throws Exception {
-        // When
-        CreationMessageId creationMessageId = CreationMessageId.of("anything-really");
-        SetMessagesRequest createMessageInDrafts = SetMessagesRequest.builder()
-                .create(
-                        creationMessageId, creationMessageBuilder.mailboxId(DRAFTS_ID.serialize()).build())
-                .build();
-        when(mockedMailboxManager.getMailbox(any(MailboxId.class), any()))
-            .thenReturn(drafts);
-        when(mockedMailboxManager.getMailboxReactive(any(MailboxId.class), any()))
-            .thenReturn(Mono.just(drafts));
-        when(mockedMailboxIdFactory.fromString(anyString())).thenReturn(DRAFTS_ID);
-        
-        sut.process(createMessageInDrafts, session);
-
-        // Then
-        verify(mockedMailSpool, never()).send(any(Mail.class), any(MailMetadata.class));
-    }
-
-    @Test
-    public void validateIsUserOwnerOfMailboxesShouldThrowWhenMailboxIdDoesntExist() throws Exception {
-        InMemoryId mailboxId = InMemoryId.of(6789);
-        when(mockedMailboxManager.getMailbox(mailboxId, session))
-            .thenThrow(new MailboxNotFoundException(mailboxId));
-        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
-            .thenReturn(mailboxId);
-
-        assertThatThrownBy(() -> sut.assertIsUserOwnerOfMailboxes(ImmutableList.of(mailboxId), session).block());
-    }
-
-    @Test
-    public void assertIsUserOwnerOfMailboxesShouldThrowWhenUserIsNotTheOwnerOfTheMailbox() throws Exception {
-        InMemoryId mailboxId = InMemoryId.of(6789);
-        MessageManager mailbox = mock(MessageManager.class);
-        when(mockedMailboxManager.getMailboxReactive(mailboxId, session))
-            .thenReturn(Mono.just(mailbox));
-        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
-            .thenReturn(mailboxId);
-        when(mailbox.getMailboxPath())
-            .thenReturn(MailboxPath.forUser(Username.of("otheruser@example.com"), mailboxId.serialize()));
-
-        assertThatThrownBy(() -> sut.assertIsUserOwnerOfMailboxes(ImmutableList.of(mailboxId), session).block())
-            .hasCauseInstanceOf(MailboxNotOwnedException.class);
-    }
-
-    @Test
-    public void assertIsUserOwnerOfMailboxesShouldNotThrowWhenUserIsTheOwnerOfTheMailbox() throws Exception {
-        InMemoryId mailboxId = InMemoryId.of(6789);
-        MessageManager mailbox = mock(MessageManager.class);
-        when(mockedMailboxManager.getMailboxReactive(mailboxId, session))
-            .thenReturn(Mono.just(mailbox));
-        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
-            .thenReturn(mailboxId);
-        when(mailbox.getMailboxPath())
-            .thenReturn(MailboxPath.forUser(USER, mailboxId.serialize()));
-
-        sut.assertIsUserOwnerOfMailboxes(ImmutableList.of(mailboxId), session).block();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldThrowWhenSenderIsNotTheConnectedUser() {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("other")
-            .email("other@example.com")
-            .build();
-
-        assertThatThrownBy(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .hasCauseInstanceOf(MailboxSendingNotAllowedException.class);
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldNotThrowWhenSenderIsTheConnectedUser() {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("user")
-            .email(USER.asString())
-            .build();
-
-        assertThatCode(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldThrowWhenSenderIsAnAliasOfAnotherUser() throws Exception {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("alias")
-            .email("alias@example.com")
-            .build();
-
-        recipientRewriteTable.addAliasMapping(MappingSource.fromUser("alias", "example.com"), OTHER_USER.asString());
-
-        assertThatThrownBy(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .hasCauseInstanceOf(MailboxSendingNotAllowedException.class);
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldNotThrowWhenSenderIsAnAliasOfTheConnectedUser() throws RecipientRewriteTableException {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("alias")
-            .email("alias@example.com")
-            .build();
-
-        recipientRewriteTable.addAliasMapping(MappingSource.fromUser("alias", "example.com"), USER.asString());
-
-        assertThatCode(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldNotThrowWhenSenderIsAnAliasOfTheConnectedUserFromADomainAlias() throws RecipientRewriteTableException {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("user")
-            .email("user@other.org")
-            .build();
-
-           recipientRewriteTable.addMapping(MappingSource.fromDomain(Domain.of("other.org")), Mapping.domainAlias(Domain.of("example.com")));
-
-        assertThatCode(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldThrowWhenSenderIsAnAliasOfTheConnectedUserFromAGroupAlias() throws RecipientRewriteTableException {
-        DraftEmailer sender = DraftEmailer
-            .builder()
-            .name("group")
-            .email("group@example.com")
-            .build();
-
-        recipientRewriteTable.addGroupMapping(MappingSource.fromUser("group", "example.com"), USER.asString());
-
-        assertThatThrownBy(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .hasCauseInstanceOf(MailboxSendingNotAllowedException.class);
-    }
-
-    public static class TestSystemMailboxesProvider implements SystemMailboxesProvider {
-
-        private final Supplier<Optional<MessageManager>> outboxSupplier;
-        private final Supplier<Optional<MessageManager>> draftsSupplier;
-
-        private TestSystemMailboxesProvider(Supplier<Optional<MessageManager>> outboxSupplier,
-                                            Supplier<Optional<MessageManager>> draftsSupplier) {
-            this.outboxSupplier = outboxSupplier;
-            this.draftsSupplier = draftsSupplier;
-        }
-
-        @Override
-        public Flux<MessageManager> getMailboxByRole(Role aRole, Username username) {
-            if (aRole.equals(Role.OUTBOX)) {
-                return Flux.fromStream(outboxSupplier.get().stream());
-            } else if (aRole.equals(Role.DRAFTS)) {
-                return Flux.fromStream(draftsSupplier.get().stream());
-            }
-            return Flux.empty();
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
deleted file mode 100644
index 0cb4704..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.user.memory.MemoryUsersRepository.withVirtualHosting;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.net.UnknownHostException;
-import java.util.Optional;
-import java.util.function.Supplier;
-
-import jakarta.mail.internet.AddressException;
-
-import org.apache.commons.configuration2.ex.ConfigurationException;
-import org.apache.james.UserEntityValidator;
-import org.apache.james.core.Domain;
-import org.apache.james.core.Username;
-import org.apache.james.dnsservice.api.DNSService;
-import org.apache.james.domainlist.api.DomainListException;
-import org.apache.james.domainlist.lib.DomainListConfiguration;
-import org.apache.james.domainlist.memory.MemoryDomainList;
-import org.apache.james.jmap.draft.model.CreationMessage;
-import org.apache.james.jmap.draft.model.CreationMessageId;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.jmap.draft.model.SetMessagesRequest;
-import org.apache.james.jmap.draft.model.SetMessagesResponse;
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-import org.apache.james.jmap.draft.send.MailSpool;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.SystemMailboxesProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.mailbox.model.ThreadId;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.rrt.api.AliasReverseResolver;
-import org.apache.james.rrt.api.CanSendFrom;
-import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
-import org.apache.james.rrt.api.RecipientRewriteTableException;
-import org.apache.james.rrt.lib.AliasReverseResolverImpl;
-import org.apache.james.rrt.lib.CanSendFromImpl;
-import org.apache.james.rrt.lib.MappingSource;
-import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import reactor.core.publisher.Flux;
-
-public class SetMessagesUpdateProcessorTest {
-
-
-    private static final Username USER = Username.of("user@example.com");
-    private static final Username OTHER_USER = Username.of("other@example.com");
-    private static final String OUTBOX = "outbox";
-    private static final InMemoryId OUTBOX_ID = InMemoryId.of(12345);
-    private static final String DRAFTS = "drafts";
-    private static final InMemoryId DRAFTS_ID = InMemoryId.of(12);
-    private static final Long TEST_MESSAGE_SIZE = 1L;
-
-    public static class TestSystemMailboxesProvider implements SystemMailboxesProvider {
-
-        private final Supplier<Optional<MessageManager>> outboxSupplier;
-        private final Supplier<Optional<MessageManager>> draftsSupplier;
-
-        private TestSystemMailboxesProvider(Supplier<Optional<MessageManager>> outboxSupplier,
-                                            Supplier<Optional<MessageManager>> draftsSupplier) {
-            this.outboxSupplier = outboxSupplier;
-            this.draftsSupplier = draftsSupplier;
-        }
-
-        @Override
-        public Flux<MessageManager> getMailboxByRole(Role aRole, Username username) {
-            if (aRole.equals(Role.OUTBOX)) {
-                return Flux.fromStream(outboxSupplier.get().stream());
-            } else if (aRole.equals(Role.DRAFTS)) {
-                return Flux.fromStream(draftsSupplier.get().stream());
-            }
-            return Flux.empty();
-        }
-    }
-
-    private final CreationMessage.Builder creationMessageBuilder = CreationMessage.builder()
-        .from(CreationMessage.DraftEmailer.builder().name("alice").email("alice@example.com").build())
-        .to(ImmutableList.of(CreationMessage.DraftEmailer.builder().name("bob").email("bob@example.com").build()))
-        .subject("Hey! ");
-
-    private final CreationMessageId creationMessageId = CreationMessageId.of("dlkja");
-
-    private final SetMessagesRequest createMessageInOutbox = SetMessagesRequest.builder()
-        .create(
-            creationMessageId,
-            creationMessageBuilder
-                .mailboxId(OUTBOX_ID.serialize())
-                .from(CreationMessage.DraftEmailer.builder().name("user").email("user@example.com").build())
-                .build())
-        .build();
-
-    private MailSpool mockedMailSpool;
-    private SystemMailboxesProvider fakeSystemMailboxesProvider;
-    private MailboxSession session;
-    private MailboxManager mockedMailboxManager;
-    private MailboxId.Factory mockedMailboxIdFactory;
-    private MemoryRecipientRewriteTable recipientRewriteTable;
-    private CanSendFrom canSendFrom;
-    private SetMessagesUpdateProcessor sut;
-    private MessageIdManager mockMessageIdManager;
-    private MessageManager outbox;
-    private MessageManager drafts;
-    private Optional<MessageManager> optionalOutbox;
-    private Optional<MessageManager> optionalDrafts;
-
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-    private MessageSender messageSender;
-    private ReferenceUpdater referenceUpdater;
-
-    @Before
-    public void setUp() throws MailboxException, DomainListException, UnknownHostException, ConfigurationException {
-        BlobManager blobManager = mock(BlobManager.class);
-        MessageIdManager messageIdManager = mock(MessageIdManager.class);
-        recipientRewriteTable = new MemoryRecipientRewriteTable();
-
-        DNSService dnsService = mock(DNSService.class);
-        MemoryDomainList domainList = new MemoryDomainList(dnsService);
-        domainList.configure(DomainListConfiguration.DEFAULT);
-        domainList.addDomain(Domain.of("example.com"));
-        domainList.addDomain(Domain.of("other.org"));
-        recipientRewriteTable.setUsersRepository(withVirtualHosting(domainList));
-        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
-        recipientRewriteTable.setDomainList(domainList);
-        recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
-        AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
-        canSendFrom = new CanSendFromImpl(recipientRewriteTable, aliasReverseResolver);
-        mockedMailSpool = mock(MailSpool.class);
-        mockedMailboxManager = mock(MailboxManager.class);
-        mockedMailboxIdFactory = mock(MailboxId.Factory.class);
-
-        mockMessageIdManager = mock(MessageIdManager.class);
-
-        fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> optionalOutbox, () -> optionalDrafts);
-        session = MailboxSessionUtil.create(USER);
-        messageSender = new MessageSender(mockedMailSpool);
-        referenceUpdater = new ReferenceUpdater(mockMessageIdManager, mockedMailboxManager);
-
-        UpdateMessagePatchConverter updateMessagePatchConverter = null;
-        MailboxManager mailboxManager = null;
-        sut = new SetMessagesUpdateProcessor(updateMessagePatchConverter,
-            messageIdManager,
-            mailboxManager,
-            fakeSystemMailboxesProvider,
-            mockedMailboxIdFactory,
-            messageSender,
-            new RecordingMetricFactory(),
-            referenceUpdater,
-            canSendFrom);
-
-        outbox = mock(MessageManager.class);
-        when(mockedMailboxIdFactory.fromString(OUTBOX_ID.serialize()))
-            .thenReturn(OUTBOX_ID);
-        when(mockedMailboxManager.getMailbox(OUTBOX_ID, session))
-            .thenReturn(outbox);
-
-        when(outbox.getId()).thenReturn(OUTBOX_ID);
-        when(outbox.getMailboxPath()).thenReturn(MailboxPath.forUser(USER, OUTBOX));
-
-        when(outbox.appendMessage(any(MessageManager.AppendCommand.class), any(MailboxSession.class)))
-            .thenReturn(new MessageManager.AppendResult(
-                new ComposedMessageId(OUTBOX_ID, TestMessageId.of(23), MessageUid.of(1)), TEST_MESSAGE_SIZE,
-                Optional.empty(), ThreadId.fromBaseMessageId(TestMessageId.of(23))));
-
-        drafts = mock(MessageManager.class);
-        when(drafts.getId()).thenReturn(DRAFTS_ID);
-        when(drafts.getMailboxPath()).thenReturn(MailboxPath.forUser(USER, DRAFTS));
-        optionalOutbox = Optional.of(outbox);
-        optionalDrafts = Optional.of(drafts);
-    }
-
-    @Test
-    public void processShouldReturnEmptyUpdatedWhenRequestHasEmptyUpdate() {
-        SetMessagesRequest requestWithEmptyUpdate = SetMessagesRequest.builder().build();
-
-        SetMessagesResponse result = sut.process(requestWithEmptyUpdate, session);
-
-        assertThat(result.getUpdated()).isEmpty();
-        assertThat(result.getNotUpdated()).isEmpty();
-    }
-
-    @Test
-    public void processShouldReturnNonEmptyNotUpdatedWhenRequestHasInvalidUpdate() {
-        // Given
-        UpdateMessagePatchConverter mockConverter = mock(UpdateMessagePatchConverter.class);
-        UpdateMessagePatch mockInvalidPatch = mock(UpdateMessagePatch.class);
-        when(mockInvalidPatch.isValid()).thenReturn(false);
-
-        MessageProperties.MessageProperty invalidProperty = MessageProperties.MessageProperty.from;
-        ImmutableList<ValidationResult> nonEmptyValidationResult = ImmutableList.of(ValidationResult.builder()
-                .property(invalidProperty.toString()).build());
-        when(mockInvalidPatch.getValidationErrors())
-                .thenReturn(nonEmptyValidationResult);
-        when(mockConverter.fromJsonNode(any(ObjectNode.class)))
-                .thenReturn(mockInvalidPatch);
-
-
-        MailboxManager mailboxManager = null;
-        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter,
-            mockMessageIdManager,
-            mailboxManager,
-            fakeSystemMailboxesProvider,
-            mockedMailboxIdFactory,
-            messageSender,
-            new RecordingMetricFactory(),
-            referenceUpdater,
-            canSendFrom);
-        MessageId requestMessageId = TestMessageId.of(1);
-        SetMessagesRequest requestWithInvalidUpdate = SetMessagesRequest.builder()
-                .update(ImmutableMap.of(requestMessageId, JsonNodeFactory.instance.objectNode()))
-                .build();
-
-        // When
-        SetMessagesResponse result = sut.process(requestWithInvalidUpdate, session);
-
-        // Then
-        assertThat(result.getNotUpdated()).describedAs("NotUpdated should not be empty").isNotEmpty();
-        assertThat(result.getNotUpdated()).containsKey(requestMessageId);
-        assertThat(result.getNotUpdated().get(requestMessageId).getProperties()).isPresent();
-        assertThat(result.getNotUpdated().get(requestMessageId).getProperties().get()).contains(invalidProperty);
-        assertThat(result.getUpdated()).isEmpty();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldNotThrowWhenSenderIsAnAliasOfTheConnectedUser() throws RecipientRewriteTableException, AddressException {
-        Username sender = Username.of("alias@example.com");
-
-        recipientRewriteTable.addAliasMapping(MappingSource.fromUser("alias", "example.com"), USER.asString());
-
-        assertThatCode(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void assertUserCanSendFromShouldThrowWhenSenderIsAnAliasOfAnotherUser() throws RecipientRewriteTableException, AddressException {
-        Username sender = Username.of("alias@example.com");
-
-        recipientRewriteTable.addAliasMapping(MappingSource.fromUser("alias", "example.com"), OTHER_USER.asString());
-
-        assertThatThrownBy(() -> sut.assertUserCanSendFrom(USER, Optional.of(sender)).block())
-            .hasCause(new MailboxSendingNotAllowedException(USER, Optional.of(sender)));
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethodTest.java
deleted file mode 100644
index 36c4389..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethodTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.apache.james.jmap.utils.AccountIdUtil.toVacationAccountId;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.model.AccountId;
-import org.apache.james.jmap.draft.model.GetMailboxesRequest;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.methods.JmapResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.jmap.draft.model.SetError;
-import org.apache.james.jmap.draft.model.SetMailboxesRequest;
-import org.apache.james.jmap.draft.model.SetVacationRequest;
-import org.apache.james.jmap.draft.model.SetVacationResponse;
-import org.apache.james.jmap.draft.model.VacationResponse;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.apache.james.vacation.api.Vacation;
-import org.apache.james.vacation.api.VacationService;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableMap;
-
-import reactor.core.publisher.Mono;
-
-public class SetVacationResponseMethodTest {
-    private static final String WRONG_ID = "WrongId";
-    private static final String TEXT_BODY = "Text body";
-    private static final Username USERNAME = Username.of("username");
-    private static final String SUBJECT = "subject";
-
-    private SetVacationResponseMethod testee;
-    private VacationService vacationService;
-    private MethodCallId methodCallId;
-    private MailboxSession mailboxSession;
-
-    @Before
-    public void setUp() {
-        methodCallId = mock(MethodCallId.class);
-        mailboxSession = mock(MailboxSession.class);
-        vacationService = mock(VacationService.class);
-        testee = new SetVacationResponseMethod(vacationService, new DefaultMetricFactory());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullRequest() {
-        testee.processToStream(null, mock(MethodCallId.class), mock(MailboxSession.class));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullMethodCallId() {
-        testee.processToStream(mock(SetMailboxesRequest.class), null, mock(MailboxSession.class));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void processShouldThrowOnNullMailboxSession() {
-        testee.processToStream(mock(SetMailboxesRequest.class), mock(MethodCallId.class), null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void processShouldThrowOnWrongRequestType() {
-        testee.processToStream(mock(GetMailboxesRequest.class), mock(MethodCallId.class), mock(MailboxSession.class));
-    }
-
-    @Test
-    public void processShouldThrowOnEmptyMap() {
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of())
-            .build();
-
-        Stream<JmapResponse> result = testee.processToStream(setVacationRequest, methodCallId, mock(MailboxSession.class));
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .error(ErrorResponse.builder()
-                .type(SetVacationResponseMethod.INVALID_ARGUMENTS)
-                .description(SetVacationResponseMethod.INVALID_ARGUMENT_DESCRIPTION)
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-        verifyNoMoreInteractions(vacationService);
-    }
-
-    @Test
-    public void processShouldThrowIfWrongMapId() {
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of(WRONG_ID, VacationResponse.builder()
-                .id(Vacation.ID)
-                .enabled(false)
-                .textBody(Optional.of(TEXT_BODY))
-                .build()))
-            .build();
-
-        Stream<JmapResponse> result = testee.processToStream(setVacationRequest, methodCallId, mock(MailboxSession.class));
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .error(ErrorResponse.builder()
-                .type(SetVacationResponseMethod.INVALID_ARGUMENTS)
-                .description(SetVacationResponseMethod.INVALID_ARGUMENT_DESCRIPTION)
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-        verifyNoMoreInteractions(vacationService);
-    }
-
-    @Test
-    public void processShouldThrowIfMapSizeNotOne() {
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of(Vacation.ID, VacationResponse.builder()
-                    .id(Vacation.ID)
-                    .enabled(false)
-                    .textBody(Optional.of(TEXT_BODY))
-                    .build(),
-                WRONG_ID, VacationResponse.builder()
-                    .id(Vacation.ID)
-                    .enabled(false)
-                    .textBody(Optional.of(TEXT_BODY))
-                    .build()))
-            .build();
-
-        Stream<JmapResponse> result = testee.processToStream(setVacationRequest, methodCallId, mock(MailboxSession.class));
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .error(ErrorResponse.builder()
-                .type(SetVacationResponseMethod.INVALID_ARGUMENTS)
-                .description(SetVacationResponseMethod.INVALID_ARGUMENT_DESCRIPTION)
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-        verifyNoMoreInteractions(vacationService);
-    }
-
-    @Test
-    public void processShouldUpdateRepositoryUponValidRequest() {
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of(Vacation.ID, VacationResponse.builder()
-                    .id(Vacation.ID)
-                    .enabled(false)
-                    .textBody(Optional.of(TEXT_BODY))
-                    .subject(Optional.of(SUBJECT))
-                    .build()))
-            .build();
-        AccountId accountId = AccountId.fromUsername(USERNAME);
-
-        when(mailboxSession.getUser()).thenReturn(USERNAME);
-        when(vacationService.modifyVacation(eq(toVacationAccountId(accountId)), any())).thenReturn(Mono.empty());
-
-        Stream<JmapResponse> result = testee.processToStream(setVacationRequest, methodCallId, mailboxSession);
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(SetVacationResponseMethod.RESPONSE_NAME)
-            .response(SetVacationResponse.builder()
-                .updatedId(Vacation.ID)
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-
-        verify(vacationService).modifyVacation(eq(toVacationAccountId(accountId)), any());
-        verifyNoMoreInteractions(vacationService);
-    }
-
-    @Test
-    public void processShouldReturnErrorIfWrongIdIsUsedInsideVacationResponse() {
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of(Vacation.ID, VacationResponse.builder()
-                .id(WRONG_ID)
-                .textBody(Optional.of(TEXT_BODY))
-                .enabled(false)
-                .build()))
-            .build();
-        when(mailboxSession.getUser()).thenReturn(USERNAME);
-
-        Stream<JmapResponse> result = testee.processToStream(setVacationRequest, methodCallId, mailboxSession);
-
-        JmapResponse expected = JmapResponse.builder()
-            .methodCallId(methodCallId)
-            .responseName(SetVacationResponseMethod.RESPONSE_NAME)
-            .response(SetVacationResponse.builder()
-                .notUpdated(Vacation.ID, SetError.builder()
-                    .type(SetError.Type.INVALID_ARGUMENTS)
-                    .description(SetVacationResponseMethod.ERROR_MESSAGE_BASE + WRONG_ID)
-                    .build())
-                .build())
-            .build();
-        assertThat(result).containsExactly(expected);
-        verifyNoMoreInteractions(vacationService);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverterTest.java
deleted file mode 100644
index ac7ad03..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchConverterTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableSet;
-
-public class UpdateMessagePatchConverterTest {
-
-    @Test
-    public void fromJsonNodeShouldSetValidationResultWhenPatchIsInvalid() {
-
-        UpdateMessagePatchValidator stubValidator = mock(UpdateMessagePatchValidator.class);
-        when(stubValidator.isValid(any(ObjectNode.class))).thenReturn(false);
-        ImmutableSet<ValidationResult> nonEmptyValidationResult = ImmutableSet.of(ValidationResult.builder().build());
-        when(stubValidator.validate(any(ObjectNode.class))).thenReturn(nonEmptyValidationResult);
-
-        UpdateMessagePatchConverter sut = new UpdateMessagePatchConverter(null, stubValidator);
-
-        ObjectNode dummynode = JsonNodeFactory.instance.objectNode();
-        UpdateMessagePatch result = sut.fromJsonNode(dummynode);
-
-        assertThat(result).extracting(UpdateMessagePatch::getValidationErrors)
-            .asList()
-            .isNotEmpty();
-    }
-
-    @Test
-    public void fromJsonNodeShouldReturnNoErrorWhenPatchIsValid() {
-
-        UpdateMessagePatchValidator mockValidator = mock(UpdateMessagePatchValidator.class);
-        when(mockValidator.isValid(any(ObjectNode.class))).thenReturn(true);
-        when(mockValidator.validate(any(ObjectNode.class))).thenReturn(ImmutableSet.of());
-        verify(mockValidator, never()).validate(any(ObjectNode.class));
-        ObjectMapper jsonParser = new ObjectMapper();
-
-        UpdateMessagePatchConverter sut = new UpdateMessagePatchConverter(jsonParser, mockValidator);
-
-        ObjectNode dummynode = JsonNodeFactory.instance.objectNode();
-        UpdateMessagePatch result = sut.fromJsonNode(dummynode);
-
-        assertThat(result.getValidationErrors().isEmpty()).isTrue();
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidatorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidatorTest.java
deleted file mode 100644
index 42f776c..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/UpdateMessagePatchValidatorTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.methods;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.util.Set;
-
-import org.apache.james.jmap.draft.model.UpdateMessagePatch;
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class UpdateMessagePatchValidatorTest {
-    private ObjectMapperFactory objectMapperFactory;
-
-    @Before
-    public void setUp() {
-        objectMapperFactory = mock(ObjectMapperFactory.class);
-    }
-
-    @Test
-    public void validateShouldReturnPropertyNameWhenPropertyHasInvalidType() throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
-
-        when(objectMapperFactory.forParsing())
-            .thenReturn(mapper);
-
-        String jsonContent = "{ \"isUnread\" : \"123\" }";
-        ObjectNode rootNode = mapper.readValue(jsonContent, ObjectNode.class);
-
-        UpdateMessagePatchValidator sut = new UpdateMessagePatchValidator(objectMapperFactory);
-        Set<ValidationResult> result = sut.validate(rootNode);
-        assertThat(result).extracting(ValidationResult::getProperty).contains("isUnread");
-    }
-
-    @Test
-    public void isValidShouldReturnTrueWhenPatchWellFormatted() throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
-
-        when(objectMapperFactory.forParsing())
-            .thenReturn(mapper);
-
-        String jsonContent = "{ \"isUnread\" : \"true\" }";
-        ObjectNode rootNode = mapper.readValue(jsonContent, ObjectNode.class);
-
-        UpdateMessagePatchValidator sut = new UpdateMessagePatchValidator(objectMapperFactory);
-        assertThat(sut.isValid(rootNode)).isTrue();
-    }
-
-    @Test
-    public void validateShouldReturnANonEmptyResultWhenParsingThrows() throws IOException {
-        //Given
-        ObjectNode emptyRootNode = new ObjectMapper().createObjectNode();
-
-        ObjectMapper mapper = mock(ObjectMapper.class);
-        JsonGenerator jsonGenerator = null;
-        when(mapper.treeToValue(any(), eq(UpdateMessagePatch.class)))
-            .thenThrow(JsonMappingException.from(jsonGenerator, "Exception when parsing"));
-
-        when(objectMapperFactory.forParsing())
-            .thenReturn(mapper);
-
-        UpdateMessagePatchValidator sut = new UpdateMessagePatchValidator(objectMapperFactory);
-
-        // When
-        Set<ValidationResult> result = sut.validate(emptyRootNode);
-        // Then
-        assertThat(result).isNotEmpty();
-        assertThat(result).extracting(ValidationResult::getProperty).contains(ValidationResult.UNDEFINED_PROPERTY);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentAccessTokenTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentAccessTokenTest.java
deleted file mode 100644
index 2ce636a..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentAccessTokenTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import org.apache.james.core.Username;
-import org.junit.Test;
-
-public class AttachmentAccessTokenTest {
-
-    private static final String USERNAME = "username";
-    private static final String BLOB_ID = "blobId";
-    private static final String EXPIRATION_DATE_STRING = "2011-12-03T10:15:30+01:00";
-    private static final ZonedDateTime EXPIRATION_DATE = ZonedDateTime.parse(EXPIRATION_DATE_STRING, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-    private static final String SIGNATURE = "signature";
-
-    @Test
-    public void getAsStringShouldNotContainBlobId() {
-        assertThat(new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE).serialize())
-            .isEqualTo(USERNAME + AttachmentAccessToken.SEPARATOR + EXPIRATION_DATE_STRING + AttachmentAccessToken.SEPARATOR + SIGNATURE);
-    }
-
-    @Test
-    public void fromShouldDeserializeAccessToken() {
-        AttachmentAccessToken attachmentAccessToken = new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE);
-        assertThat(AttachmentAccessToken.from(attachmentAccessToken.serialize(), BLOB_ID))
-            .isEqualTo(attachmentAccessToken);
-    }
-
-    @Test
-    public void extraSpacesShouldBeIgnored() {
-        AttachmentAccessToken attachmentAccessToken = new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE);
-        assertThat(AttachmentAccessToken.from(attachmentAccessToken.serialize() + " ", BLOB_ID))
-            .isEqualTo(attachmentAccessToken);
-    }
-
-    @Test
-    public void fromShouldAcceptUsernamesWithUnderscores() {
-        Username failingUsername = Username.of("bad_separator@usage.screwed");
-        AttachmentAccessToken attachmentAccessToken = new AttachmentAccessToken(failingUsername.asString(), BLOB_ID, EXPIRATION_DATE, SIGNATURE);
-        assertThat(AttachmentAccessToken.from(attachmentAccessToken.serialize(), BLOB_ID))
-            .isEqualTo(attachmentAccessToken);
-    }
-
-    @Test
-    public void getPayloadShouldNotContainBlobId() {
-        assertThat(new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE).getPayload())
-            .isEqualTo(USERNAME + AttachmentAccessToken.SEPARATOR + EXPIRATION_DATE_STRING);
-    }
-
-    @Test
-    public void getSignedContentShouldContainBlobId() {
-        assertThat(new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE).getSignedContent())
-            .isEqualTo(BLOB_ID + AttachmentAccessToken.SEPARATOR + USERNAME + AttachmentAccessToken.SEPARATOR + EXPIRATION_DATE_STRING);
-    }
-
-    @Test
-    public void buildWithNullUsernameShouldThrow() {
-        assertThatThrownBy(() -> AttachmentAccessToken.builder()
-            .username(null)
-            .build()
-        ).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void buildWithNullBlobIdShouldThrow() {
-        assertThatThrownBy(() -> AttachmentAccessToken.builder()
-            .username(USERNAME)
-            .blobId(null)
-            .build()
-        ).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void buildWithNullExpirationDateShouldThrow() {
-        assertThatThrownBy(() -> AttachmentAccessToken.builder()
-            .username(USERNAME)
-            .blobId(BLOB_ID)
-            .expirationDate(null)
-            .build()
-        ).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void buildWithNullSignatureShouldThrow() {
-        assertThatThrownBy(() -> AttachmentAccessToken.builder()
-            .username(USERNAME)
-            .blobId(BLOB_ID)
-            .expirationDate(EXPIRATION_DATE)
-            .signature(null)
-            .build()
-        ).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void buildWithValidArgumentsShouldBuild() {
-        AttachmentAccessToken expected = new AttachmentAccessToken(USERNAME, BLOB_ID, EXPIRATION_DATE, SIGNATURE);
-        AttachmentAccessToken actual = AttachmentAccessToken.builder()
-            .username(USERNAME)
-            .blobId(BLOB_ID)
-            .expirationDate(EXPIRATION_DATE)
-            .signature(SIGNATURE)
-            .build();
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/ContinuationTokenTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/ContinuationTokenTest.java
deleted file mode 100644
index a1b82c6..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/ContinuationTokenTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import org.apache.james.jmap.draft.exceptions.MalformedContinuationTokenException;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ContinuationTokenTest {
-
-    private static final String USER = "user";
-    private static final String EXPIRATION_DATE_STRING = "2011-12-03T10:15:30+01:00";
-    public static final String SIGNATURE = "signature";
-
-    private ZonedDateTime expirationDate;
-
-    @Before
-    public void setUp() {
-        expirationDate = ZonedDateTime.parse(EXPIRATION_DATE_STRING, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-    }
-
-    @Test
-    public void continuationTokenShouldBeRetrievedFromString() throws Exception {
-        assertThat(ContinuationToken.fromString(USER + ContinuationToken.SEPARATOR + EXPIRATION_DATE_STRING + ContinuationToken.SEPARATOR + SIGNATURE))
-            .isEqualTo(new ContinuationToken(USER, expirationDate, SIGNATURE));
-    }
-
-    @Test
-    public void usernameShouldBeAllowedToContainSeparator() throws Exception {
-        String username = "user" + ContinuationToken.SEPARATOR + "using" + ContinuationToken.SEPARATOR + "separator";
-        assertThat(ContinuationToken.fromString(username + ContinuationToken.SEPARATOR + EXPIRATION_DATE_STRING + ContinuationToken.SEPARATOR + SIGNATURE))
-            .isEqualTo(new ContinuationToken(username, expirationDate, SIGNATURE));
-    }
-
-    @Test(expected = MalformedContinuationTokenException.class)
-    public void continuationTokenThatMissPartsShouldThrow() throws Exception {
-        ContinuationToken.fromString(EXPIRATION_DATE_STRING + ContinuationToken.SEPARATOR + SIGNATURE);
-    }
-
-    @Test(expected = MalformedContinuationTokenException.class)
-    public void continuationTokenWithMalformedDatesShouldThrow() throws Exception {
-        ContinuationToken.fromString(USER + ContinuationToken.SEPARATOR + "2011-25-03T10:15:30+01:00" + ContinuationToken.SEPARATOR + SIGNATURE);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void nullContinuationTokenShouldThrow() throws Exception {
-        ContinuationToken.fromString(null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void emptyContinuationTokenShouldThrow() throws Exception {
-        ContinuationToken.fromString("");
-    }
-
-    @Test
-    public void getAsStringShouldReturnACorrectResult() throws Exception {
-        assertThat(new ContinuationToken(USER, expirationDate, SIGNATURE).serialize())
-            .isEqualTo(USER + ContinuationToken.SEPARATOR + EXPIRATION_DATE_STRING + ContinuationToken.SEPARATOR + SIGNATURE);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void newContinuationTokenWithNullUsernameShouldThrow() {
-        new ContinuationToken(null, expirationDate, SIGNATURE);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void newContinuationTokenWithNullExpirationDateShouldThrow() {
-        new ContinuationToken(USER, null, SIGNATURE);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void newContinuationTokenWithNullSignatureShouldThrow() {
-        new ContinuationToken(USER, expirationDate, null);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/CreationMessageTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/CreationMessageTest.java
deleted file mode 100644
index 0423329..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/CreationMessageTest.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.draft.methods.ValidationResult;
-import org.apache.james.jmap.draft.model.CreationMessage.DraftEmailer;
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class CreationMessageTest {
-
-    private CreationMessage.Builder testedBuilder;
-
-    @Before
-    public void setUp() {
-        testedBuilder = CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("ba9-0f-dead-beef"))
-                .headers(ImmutableMap.of());
-    }
-
-    @Test
-    public void buildShouldThrowWhenBothMapAndOldKeyword() {
-        assertThatThrownBy(() -> CreationMessage.builder()
-                .mailboxIds(ImmutableList.of("ba9-0f-dead-beef"))
-                .headers(ImmutableMap.of())
-                .keywords(ImmutableMap.of("$Draft", true))
-                .isAnswered(Optional.of(true))
-                .build())
-            .isInstanceOf(IllegalArgumentException.class)
-            .hasMessage("Does not support keyword and is* at the same time");
-    }
-
-    @Test
-    public void validateShouldReturnErrorWhenFromIsMissing() {
-       testedBuilder = testedBuilder
-               .subject("anything");
-
-        CreationMessage sut = testedBuilder.build();
-
-        assertThat(sut.validate()).contains(ValidationResult.builder()
-                .message("'from' address is mandatory")
-                .property(MessageProperty.from.asFieldName())
-                .build()
-        );
-    }
-
-    @Test
-    public void validateShouldReturnErrorWhenFromIsInvalid() {
-        testedBuilder = testedBuilder
-                .subject("anything");
-
-        CreationMessage sut = testedBuilder.from(DraftEmailer.builder().name("bob").email("bob@domain.com@example.com").build()).build();
-
-        assertThat(sut.validate()).contains(ValidationResult.builder()
-                .message("'from' address is mandatory")
-                .property(MessageProperty.from.asFieldName())
-                .build()
-        );
-    }
-
-    @Test
-    public void validateShouldReturnErrorWhenNoRecipientSet() {
-        testedBuilder = testedBuilder
-                .subject("anything");
-
-        CreationMessage  sut = testedBuilder
-                .from(DraftEmailer.builder().name("bob").email("bob@example.com").build()).build();
-
-        assertThat(sut.validate()).extracting(ValidationResult::getErrorMessage).contains("no recipient address set");
-    }
-
-    @Test
-    public void validateShouldReturnErrorWhenNoValidRecipientSet() {
-        testedBuilder = testedBuilder
-                .subject("anything");
-
-        CreationMessage sut = testedBuilder
-                .from(DraftEmailer.builder().name("bob").email("bob@example.com").build())
-                .to(ImmutableList.of(DraftEmailer.builder().name("riri").email("riri@acme.com@example.com").build()))
-                .cc(ImmutableList.of(DraftEmailer.builder().name("fifi").email("fifi@acme.com@example.com").build()))
-                .bcc(ImmutableList.of(DraftEmailer.builder().name("loulou").email("loulou@acme.com@example.com").build()))
-                .build();
-
-        assertThat(sut.validate()).extracting(ValidationResult::getErrorMessage).contains("no recipient address set");
-    }
-
-    @Test
-    public void validateShouldReturnEmptyListWhenNoErrors() {
-        testedBuilder = testedBuilder
-                .subject("anything");
-
-        CreationMessage sut = testedBuilder
-                .from(DraftEmailer.builder().name("bob").email("bob@example.com").build())
-                .to(ImmutableList.of(DraftEmailer.builder().name("riri").email("riri@example.com").build()))
-                .build();
-
-        assertThat(sut.validate()).isEmpty();
-    }
-
-    @Test
-    public void validateShouldReturnEmptyListWhenSubjectIsNull() {
-        testedBuilder = testedBuilder
-                .subject(null);
-
-        CreationMessage sut = testedBuilder
-                .from(DraftEmailer.builder().name("bob").email("bob@example.com").build())
-                .to(ImmutableList.of(DraftEmailer.builder().name("riri").email("riri@example.com").build()))
-                .build();
-
-        assertThat(sut.validate()).isEmpty();
-    }
-
-    @Test
-    public void validateShouldReturnEmptyListWhenSubjectIsEmpty() {
-        testedBuilder = testedBuilder
-                .subject("");
-
-        CreationMessage sut = testedBuilder
-                .from(DraftEmailer.builder().name("bob").email("bob@example.com").build())
-                .to(ImmutableList.of(DraftEmailer.builder().name("riri").email("riri@example.com").build()))
-                .build();
-
-        assertThat(sut.validate()).isEmpty();
-    }
-
-    @Test
-    public void mailboxIdShouldSetASingletonList() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .headers(ImmutableMap.of())
-            .mailboxId(mailboxId)
-            .build();
-
-        assertThat(message.getMailboxIds()).containsExactly(mailboxId);
-    }
-
-    @Test
-    public void isDraftShouldBeFalseWhenNoKeywordsSpecified() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .mailboxId(mailboxId)
-            .build();
-
-        assertThat(message.isDraft()).isFalse();
-    }
-
-    @Test
-    public void isDraftShouldBeTrueWhenOldKeywordDraft() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .mailboxId(mailboxId)
-            .isDraft(Optional.of(true))
-            .build();
-
-        assertThat(message.isDraft()).isTrue();
-    }
-
-    @Test
-    public void isDraftShouldBeFalseWhenOldKeywordNonDraft() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .mailboxId(mailboxId)
-            .isAnswered(Optional.of(true))
-            .build();
-
-        assertThat(message.isDraft()).isFalse();
-    }
-
-    @Test
-    public void isDraftShouldBeFalseWhenEmptyKeywords() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .keywords(ImmutableMap.of())
-            .mailboxId(mailboxId)
-            .build();
-
-        assertThat(message.isDraft()).isFalse();
-    }
-
-    @Test
-    public void isDraftShouldBeFalseWhenKeywordsDoesNotContainsDraft() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .keywords(ImmutableMap.of(Keyword.ANSWERED.getFlagName(), true))
-            .mailboxId(mailboxId)
-            .build();
-
-        assertThat(message.isDraft()).isFalse();
-    }
-
-    @Test
-    public void isDraftShouldBeTrueWhenKeywordsContainsDraft() {
-        String mailboxId = "123";
-        CreationMessage message = CreationMessage.builder()
-            .keywords(ImmutableMap.of(Keyword.DRAFT.getFlagName(), true))
-            .mailboxId(mailboxId)
-            .build();
-
-        assertThat(message.isDraft()).isTrue();
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterConditionTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterConditionTest.java
deleted file mode 100644
index c983641..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterConditionTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-import org.apache.james.jmap.model.Number;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.google.common.collect.ImmutableList;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class FilterConditionTest {
-
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
-    @Test
-    public void buildShouldWorkWhenNoInMailboxes() {
-        FilterCondition filterCondition = FilterCondition.builder().build();
-        assertThat(filterCondition.getInMailboxes()).isEmpty();
-    }
-
-    @Test
-    public void buildShouldWorkWhenGivenInMailboxes() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .inMailboxes(Optional.of(ImmutableList.of("1", "2")))
-                .build();
-        assertThat(filterCondition.getInMailboxes()).contains(ImmutableList.of("1", "2"));
-    }
-
-    @Test
-    public void buildShouldWorkWhenGivenInMailboxesAsEllipsis() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .inMailboxes("1", "2")
-                .build();
-        assertThat(filterCondition.getInMailboxes()).contains(ImmutableList.of("1", "2"));
-    }
-
-    @Test
-    public void builderShouldBuildWhenGivenNotInMailboxes() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .notInMailboxes(Optional.of(ImmutableList.of("1", "2")))
-                .build();
-        assertThat(filterCondition.getNotInMailboxes()).contains(ImmutableList.of("1", "2"));
-    }
-
-    @Test
-    public void builderShouldBuildWhenGivenNotInMailboxesAsEllipsis() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .notInMailboxes("1", "2")
-                .build();
-        assertThat(filterCondition.getNotInMailboxes()).contains(ImmutableList.of("1", "2"));
-    }
-
-    @Test
-    public void buildShouldWork() {
-        ZonedDateTime before = ZonedDateTime.parse("2016-07-19T14:30:00Z");
-        ZonedDateTime after = ZonedDateTime.parse("2016-07-19T14:31:00Z");
-        long minSize = 4;
-        long maxSize = 123;
-        boolean isFlagged = true;
-        boolean isUnread = true;
-        boolean isAnswered = true;
-        boolean isDraft = true;
-        boolean isForwarded = true;
-        boolean hasAttachment = true;
-        String text = "text";
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        String cc = "copy@james.org";
-        String bcc = "blindcopy@james.org";
-        String subject = "subject";
-        String body = "body";
-        String attachments = "attachments";
-        Header header = Header.from(ImmutableList.of("name", "value"));
-        Optional<String> hasKeyword = Optional.of("$Draft");
-        Optional<String> notKeyword = Optional.of("$Flagged");
-        Optional<String> attachmentFileName = Optional.of("file.txt");
-
-        FilterCondition expectedFilterCondition = new FilterCondition(Optional.of(ImmutableList.of("1")), Optional.of(ImmutableList.of("2")), Optional.of(before), Optional.of(after),
-                Optional.of(Number.fromLong(minSize)), Optional.of(Number.fromLong(maxSize)),
-                Optional.of(isFlagged), Optional.of(isUnread), Optional.of(isAnswered), Optional.of(isDraft), Optional.of(isForwarded), Optional.of(hasAttachment), Optional.of(text), Optional.of(from),
-                Optional.of(to), Optional.of(cc), Optional.of(bcc), Optional.of(subject), Optional.of(body), Optional.of(attachments), Optional.of(header),
-                hasKeyword, notKeyword, attachmentFileName);
-
-        FilterCondition filterCondition = FilterCondition.builder()
-                .inMailboxes(Optional.of(ImmutableList.of("1")))
-                .notInMailboxes("2")
-                .before(before)
-                .after(after)
-                .minSize(minSize)
-                .maxSize(maxSize)
-                .isFlagged(isFlagged)
-                .isUnread(isUnread)
-                .isAnswered(isAnswered)
-                .isDraft(isDraft)
-                .isForwarded(isForwarded)
-                .hasAttachment(hasAttachment)
-                .text(text)
-                .from(from)
-                .to(to)
-                .cc(cc)
-                .bcc(bcc)
-                .subject(subject)
-                .body(body)
-                .attachments(attachments)
-                .header(header)
-                .hasKeyword(hasKeyword)
-                .notKeyword(notKeyword)
-                .attachmentFileName(attachmentFileName)
-                .build();
-
-        assertThat(filterCondition).isEqualToComparingFieldByField(expectedFilterCondition);
-    }
-
-    @Test
-    public void shouldRespectJavaBeanContract() {
-        EqualsVerifier.forClass(FilterCondition.class).verify();
-    }
-
-    @Test
-    public void buildShouldBuildFilterConditionWithHasKeywordWhenGivenHasKeyword() {
-        String hasKeyword = "$Draft";
-
-        FilterCondition filterCondition = FilterCondition.builder()
-            .hasKeyword(Optional.of(hasKeyword))
-            .build();
-
-        assertThat(filterCondition.getHasKeyword().get())
-            .isEqualTo(hasKeyword);
-    }
-
-    @Test
-    public void buildShouldBuildFilterConditionWithoutHasKeywordWhenDoNotGivenHasKeyword() {
-        FilterCondition filterCondition = FilterCondition.builder()
-            .hasKeyword(Optional.empty())
-            .build();
-
-        assertThat(filterCondition.getHasKeyword().isPresent())
-            .isFalse();
-    }
-
-    @Test
-    public void buildShouldThrowWhenGivenInvalidKeywordAsHasKeyword() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        FilterCondition.builder()
-            .hasKeyword(Optional.of("$Draft%"))
-            .build();
-    }
-
-    @Test
-    public void buildShouldBuildFilterConditionWithNotKeywordWhenGivenNotKeyword() {
-        String notKeyword = "$Draft";
-
-        FilterCondition filterCondition = FilterCondition.builder()
-            .notKeyword(Optional.of(notKeyword))
-            .build();
-        assertThat(filterCondition.getNotKeyword().get()).isEqualTo(notKeyword);
-    }
-
-    @Test
-    public void buildShouldBuildFilterConditionWithoutNotKeywordWhenDoNotGivenNotKeyword() {
-        FilterCondition filterCondition = FilterCondition.builder()
-            .notKeyword(Optional.empty())
-            .build();
-
-        assertThat(filterCondition.getNotKeyword().isPresent())
-            .isFalse();
-    }
-
-    @Test
-    public void buildShouldThrowWhenGivenInvalidKeywordAsNotKeyword() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        FilterCondition.builder()
-            .notKeyword(Optional.of("$Draft%"))
-            .build();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterOperatorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterOperatorTest.java
deleted file mode 100644
index e8607e4..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterOperatorTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.junit.Test;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class FilterOperatorTest {
-
-    @Test(expected = IllegalStateException.class)
-    public void builderShouldThrowWhenOperatorIsNotGiven() {
-        FilterOperator.builder().build();
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builderShouldThrowWhenOperatorIsNull() {
-        FilterOperator.builder().operator(null);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void builderShouldThrowWhenConditionsIsEmpty() {
-        FilterOperator.builder().operator(Operator.AND).build();
-    }
-
-    @Test
-    public void builderShouldWork() {
-        ImmutableList<Filter> conditions = ImmutableList.of(FilterCondition.builder().build());
-        FilterOperator expectedFilterOperator = new FilterOperator(Operator.AND, conditions);
-
-        FilterOperator filterOperator = FilterOperator.builder()
-            .operator(Operator.AND)
-            .conditions(conditions)
-            .build();
-
-        assertThat(filterOperator).isEqualToComparingFieldByField(expectedFilterOperator);
-    }
-
-    @Test
-    public void andFactoryMethodShouldThrowWhenNoArgument() {
-        assertThatThrownBy(FilterOperator::and).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void andFactoryMethodShouldReturnRightOperator() {
-        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
-        ImmutableList<Filter> conditions = ImmutableList.of(condition);
-        FilterOperator expectedFilterOperator = new FilterOperator(Operator.AND, conditions);
-        assertThat(FilterOperator.and(condition)).isEqualTo(expectedFilterOperator);
-    }
-
-    @Test
-    public void orFactoryMethodShouldThrowWhenNoArgument() {
-        assertThatThrownBy(FilterOperator::or).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void orFactoryMethodShouldReturnRightOperator() {
-        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
-        ImmutableList<Filter> conditions = ImmutableList.of(condition);
-        FilterOperator expectedFilterOperator = new FilterOperator(Operator.OR, conditions);
-        assertThat(FilterOperator.or(condition)).isEqualTo(expectedFilterOperator);
-    }
-
-    @Test
-    public void notFactoryMethodShouldThrowWhenNoArgument() {
-        assertThatThrownBy(FilterOperator::not).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void notFactoryMethodShouldReturnRightOperator() {
-        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
-        ImmutableList<Filter> conditions = ImmutableList.of(condition);
-        FilterOperator expectedFilterOperator = new FilterOperator(Operator.NOT, conditions);
-        assertThat(FilterOperator.not(condition)).isEqualTo(expectedFilterOperator);
-    }
-
-    @Test
-    public void shouldRespectJavaBeanContract() {
-        EqualsVerifier.forClass(FilterOperator.class).verify();
-    }
-
-    @Test
-    public void toStringShouldBePretty() {
-        FilterOperator testee = 
-                FilterOperator.and(
-                    FilterCondition.builder().inMailboxes("12","34").build(),
-                    FilterOperator.or(
-                        FilterOperator.not(
-                            FilterCondition.builder().notInMailboxes("45").build()),
-                        FilterCondition.builder().build()));
-                
-        String expected = Joiner.on('\n').join(
-                            "FilterOperator{operator=AND}",
-                            "  FilterCondition{inMailboxes=[12, 34]}",
-                            "  FilterOperator{operator=OR}",
-                            "    FilterOperator{operator=NOT}",
-                            "      FilterCondition{notInMailboxes=[45]}",
-                            "    FilterCondition{}",
-                            "");
-        String actual = testee.toString();
-        assertThat(actual).isEqualTo(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterTest.java
deleted file mode 100644
index 9098f0d..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/FilterTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.stream.IntStream;
-
-import org.apache.james.jmap.json.ObjectMapperFactory;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class FilterTest {
-
-    private ObjectMapper parser;
-
-    @Before
-    public void setup() {
-        parser = new ObjectMapperFactory(new InMemoryId.Factory(), new InMemoryMessageId.Factory()).forParsing();
-    }
-
-    @Test
-    public void emptyFilterConditionShouldBeDeserialized() throws Exception {
-        String json = "{}";
-        Filter expected = FilterCondition.builder()
-                .build();
-        Filter actual = parser.readValue(json, Filter.class);
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    public void singleFilterConditionShouldBeDeserialized() throws Exception {
-        String json = "{\"inMailboxes\": [\"12\",\"34\"]}";
-        Filter expected = FilterCondition.builder().inMailboxes("12","34").build();
-        Filter actual = parser.readValue(json, Filter.class);
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    public void doubleFilterConditionShouldBeDeserialized() throws Exception {
-        String json = "{\"inMailboxes\": [\"12\",\"34\"], \"notInMailboxes\": [\"45\",\"67\"]}";
-        Filter expected = FilterCondition.builder()
-                .inMailboxes("12","34")
-                .notInMailboxes("45","67")
-                .build();
-        Filter actual = parser.readValue(json, Filter.class);
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    public void operatorWithSingleConditionShouldBeDeserialized() throws Exception {
-        String json = "{\"operator\": \"AND\", \"conditions\": [{\"inMailboxes\": [\"12\",\"34\"]}]}";
-        Filter expected = FilterOperator.and(FilterCondition.builder().inMailboxes("12","34").build());
-        Filter actual = parser.readValue(json, Filter.class);
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    public void complexFilterShouldBeDeserialized() throws Exception {
-        String json = "{\"operator\": \"AND\", \"conditions\": ["
-                + "         {\"inMailboxes\": [\"12\",\"34\"]},"
-                + "         {\"operator\": \"OR\", \"conditions\": ["
-                + "                 {\"operator\": \"NOT\", \"conditions\": ["
-                + "                         {\"notInMailboxes\": [\"45\"]}]},"
-                + "                 {}]}]}";
-        Filter expected = 
-                FilterOperator.and(
-                        FilterCondition.builder().inMailboxes("12","34").build(),
-                        FilterOperator.or(
-                                FilterOperator.not(
-                                        FilterCondition.builder().notInMailboxes("45").build()),
-                                FilterCondition.builder().build()));
-        Filter actual = parser.readValue(json, Filter.class);
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    public void flattenShouldNoopWhenCondition() {
-        FilterCondition condition = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-
-        assertThat(condition.breadthFirstVisit(10))
-            .containsExactly(condition);
-    }
-
-    @Test
-    public void breadthFirstVisitShouldUnboxOneLevelOperator() {
-        FilterCondition condition1 = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-        FilterCondition condition2 = FilterCondition.builder()
-            .to("alice@domain.tld")
-            .build();
-
-        assertThat(FilterOperator.and(condition1, condition2)
-                .breadthFirstVisit())
-            .containsExactly(condition1, condition2);
-    }
-
-    @Test
-    public void breadthFirstVisitShouldUnboxTwoLevelOperator() {
-        FilterCondition condition1 = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-        FilterCondition condition2 = FilterCondition.builder()
-            .to("alice@domain.tld")
-            .build();
-        FilterCondition condition3 = FilterCondition.builder()
-            .to("cedric@domain.tld")
-            .build();
-
-        assertThat(FilterOperator.and(condition1, FilterOperator.and(condition2, condition3))
-                .breadthFirstVisit())
-            .containsOnly(condition1, condition2, condition3);
-    }
-
-    @Test
-    public void breadthFirstVisitShouldBeBreadthFirst() {
-        FilterCondition condition1 = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-        FilterCondition condition2 = FilterCondition.builder()
-            .to("alice@domain.tld")
-            .build();
-        FilterCondition condition3 = FilterCondition.builder()
-            .to("cedric@domain.tld")
-            .build();
-        FilterCondition condition4 = FilterCondition.builder()
-            .to("david@domain.tld")
-            .build();
-
-        assertThat(FilterOperator.and(condition1, FilterOperator.and(condition2, condition3), condition4)
-                .breadthFirstVisit())
-            .containsOnly(condition1, condition2, condition3, condition4);
-    }
-
-    @Test
-    public void breadthFirstVisitShouldAllowUpToLimitNesting() {
-        FilterCondition condition = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-
-        Filter nestedFilter = IntStream.range(0, 10).boxed().reduce(
-            (Filter) condition,
-            (filter, i) -> FilterOperator.and(filter),
-            (f1, f2) -> {
-                throw new RuntimeException("unsupported combination");
-            });
-
-        assertThat(nestedFilter.breadthFirstVisit())
-            .containsExactly(condition);
-    }
-
-    @Test
-    public void breadthFirstVisitShouldRejectDeepNesting() {
-        FilterCondition condition = FilterCondition.builder()
-            .to("bob@domain.tld")
-            .build();
-
-        Filter nestedFilter = IntStream.range(0, 11).boxed().reduce(
-            (Filter) condition,
-            (filter, i) -> FilterOperator.and(filter),
-            (f1, f2) -> {
-                throw new RuntimeException("unsupported combination");
-            });
-
-        assertThatThrownBy(nestedFilter::breadthFirstVisit)
-            .isInstanceOf(Filter.TooDeepFilterHierarchyException.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxMessageListResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxMessageListResponseTest.java
deleted file mode 100644
index 2a59802..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxMessageListResponseTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.james.jmap.model.Number;
-
-public class GetMailboxMessageListResponseTest {
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAccountId() {
-        GetMessageListResponse.builder().accountId(null);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenCollapseThreads() {
-        GetMessageListResponse.builder().collapseThreads(false);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenState() {
-        GetMessageListResponse.builder().state(null);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenCanCalculateUpdates() {
-        GetMessageListResponse.builder().canCalculateUpdates(false);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenPosition() {
-        GetMessageListResponse.builder().position(0);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenTotal() {
-        GetMessageListResponse.builder().total(0);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenThreadIds() {
-        GetMessageListResponse.builder().threadIds(null);
-    }
-    
-    @Test
-    public void builderShouldWork() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .inMailboxes(Optional.of(ImmutableList.of("1", "2")))
-                .build();
-        List<String> sort = ImmutableList.of("date desc");
-        List<MessageId> messageIds = ImmutableList.of(TestMessageId.of(3), TestMessageId.of(4));
-        GetMessageListResponse expectedGetMessageListResponse = new GetMessageListResponse(null, filterCondition, sort, false, null, false,
-            Number.ZERO, Number.ZERO, ImmutableList.of(), messageIds);
-
-        GetMessageListResponse getMessageListResponse = GetMessageListResponse.builder()
-            .filter(filterCondition)
-            .sort(sort)
-            .messageIds(messageIds)
-            .build();
-        assertThat(getMessageListResponse).isEqualToComparingFieldByField(expectedGetMessageListResponse);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxesRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxesRequestTest.java
deleted file mode 100644
index 70cd552..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMailboxesRequestTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class GetMailboxesRequestTest {
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAccountId() {
-        GetMailboxesRequest.builder().accountId("1");
-    }
-
-    @Test
-    public void builderShouldNotThrowWhenIds() {
-        GetMailboxesRequest.builder().ids(ImmutableList.of());
-    }
-
-    @Test
-    public void idsShouldBeEmptyListWhenEmptyList() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-            .ids(ImmutableList.of())
-            .build();
-        assertThat(getMailboxesRequest.getIds()).isPresent();
-        assertThat(getMailboxesRequest.getIds().get()).hasSize(0);
-    }
-
-    @Test
-    public void idsShouldBePresentWhenListIsNotEmpty() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-            .ids(ImmutableList.of(InMemoryId.of(123)))
-            .build();
-        assertThat(getMailboxesRequest.getIds()).isPresent();
-        assertThat(getMailboxesRequest.getIds().get()).containsExactly(InMemoryId.of(123));
-    }
-
-    @Test
-    public void propertiesShouldBeEmptyWhenNotGiven() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder().build();
-        assertThat(getMailboxesRequest.getProperties()).isEmpty();
-    }
-
-    @Test
-    public void propertiesShouldNotBeEmptyWhenEmptyListGiven() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .properties(ImmutableList.of())
-                .build();
-
-        assertThat(getMailboxesRequest.getProperties()).isPresent();
-        assertThat(getMailboxesRequest.getProperties().get()).isEmpty();;
-    }
-
-    @Test
-    public void propertiesShouldNotBeEmptyWhenListGiven() {
-        GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()
-                .properties(ImmutableList.of("id"))
-                .build();
-
-        assertThat(getMailboxesRequest.getProperties()).isPresent();
-        assertThat(getMailboxesRequest.getProperties().get()).containsOnly(MailboxProperty.ID);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessageListRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessageListRequestTest.java
deleted file mode 100644
index a480353..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessageListRequestTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.james.jmap.model.Number;
-
-public class GetMessageListRequestTest {
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builderShouldThrowWhenPositionIsNegative() {
-        GetMessageListRequest.builder().position(-1L);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builderShouldThrowWhenLimitIsNegative() {
-        GetMessageListRequest.builder().limit(-1);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAccountId() {
-        GetMessageListRequest.builder().accountId(null);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAnchor() {
-        GetMessageListRequest.builder().anchor(null);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAnchorOffset() {
-        GetMessageListRequest.builder().anchorOffset(0);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenFetchThreads() {
-        GetMessageListRequest.builder().fetchThreads(false);
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenFetchSearchSnippets() {
-        GetMessageListRequest.builder().fetchSearchSnippets(false);
-    }
-
-    @Test
-    public void builderShouldWork() {
-        FilterCondition filterCondition = FilterCondition.builder()
-                .inMailboxes(Optional.of(ImmutableList.of("1", "2")))
-                .build();
-        List<String> sort = ImmutableList.of("date desc");
-        List<String> fetchMessageProperties = ImmutableList.of("id", "blobId");
-        GetMessageListRequest expectedGetMessageListRequest = new GetMessageListRequest(Optional.empty(), Optional.of(filterCondition), sort, Optional.of(true), Optional.of(Number.fromLong(1L)), Optional.empty(), Optional.empty(), Optional.of(Number.fromLong(2)),
-                Optional.empty(), Optional.of(true), fetchMessageProperties, Optional.empty());
-
-        GetMessageListRequest getMessageListRequest = GetMessageListRequest.builder()
-            .filter(filterCondition)
-            .sort(sort)
-            .collapseThreads(true)
-            .position(1L)
-            .limit(2)
-            .fetchMessages(true)
-            .fetchMessageProperties(fetchMessageProperties)
-            .build();
-
-        assertThat(getMessageListRequest).isEqualToComparingFieldByField(expectedGetMessageListRequest);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessagesRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessagesRequestTest.java
deleted file mode 100644
index 4a8748c..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetMessagesRequestTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-public class GetMessagesRequestTest {
-
-    @Test
-    public void shouldAllowOptionalAccountId() {
-        GetMessagesRequest result = GetMessagesRequest.builder()
-                .ids(ImmutableList.of(TestMessageId.of(1)))
-                .build();
-        assertThat(result).isNotNull();
-        assertThat(result.getAccountId()).isEmpty();
-    }
-
-    @Test
-    public void shouldThrowWhenAccountIdIsNull() {
-        assertThatThrownBy(() -> GetMessagesRequest.builder().accountId(null))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void shouldAllowEmptyMessagesList() {
-        GetMessagesRequest result = GetMessagesRequest.builder()
-                .accountId("accountId")
-                .ids(ImmutableList.of())
-                .build();
-        assertThat(result).isNotNull();
-        assertThat(result.getIds()).isEmpty();
-    }
-
-    @Test
-    public void shouldAllowAbsentPropertyList() {
-        GetMessagesRequest result = GetMessagesRequest.builder()
-                .accountId("accountId")
-                .ids(ImmutableList.of())
-                .build();
-        assertThat(result).isNotNull();
-        assertThat(result.getProperties().getOptionalMessageProperties()).isEmpty();
-        assertThat(result.getProperties().getOptionalHeadersProperties()).isEmpty();
-    }
-
-    @Test
-    public void shouldAllowEmptyPropertyList() {
-        GetMessagesRequest result = GetMessagesRequest.builder()
-                .accountId("accountId")
-                .ids(ImmutableList.of())
-                .properties(ImmutableList.of())
-                .build();
-        assertThat(result).isNotNull();
-        assertThat(result.getProperties().getOptionalMessageProperties()).hasValue(ImmutableSet.of());
-        assertThat(result.getProperties().getOptionalHeadersProperties()).hasValue(ImmutableSet.of());
-    }
-
-    @Test
-    public void shouldConvertPropertiesWhenMessageAndHeaderPropertiesAreGiven() {
-        GetMessagesRequest result = GetMessagesRequest.builder()
-                .accountId("accountId")
-                .ids(ImmutableList.of())
-                .properties(ImmutableList.of("id", "headers.subject", "threadId", "headers.test"))
-                .build();
-        assertThat(result).isNotNull();
-        assertThat(result.getProperties().getOptionalMessageProperties()).hasValue(ImmutableSet.of(MessageProperty.id, MessageProperty.threadId));
-        assertThat(result.getProperties().getOptionalHeadersProperties()).hasValue(ImmutableSet.of(HeaderProperty.valueOf("headers.subject"), HeaderProperty.valueOf("headers.test")));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationRequestTest.java
deleted file mode 100644
index f5ce918..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationRequestTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.junit.Test;
-
-public class GetVacationRequestTest {
-
-    @Test(expected = NotImplementedException.class)
-    public void accountIdHandlingIsNotImplementedYet() {
-        GetVacationRequest.builder()
-            .accountId("any");
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationResponseTest.java
deleted file mode 100644
index 5e605e1..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/GetVacationResponseTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import org.junit.Test;
-
-public class GetVacationResponseTest {
-
-    public static final String IDENTIFIER = "identifier";
-
-    @Test
-    public void getVacationResponseShouldBeConstructedWithTheRightInformation() {
-        VacationResponse vacationResponse = VacationResponse.builder()
-            .textBody(Optional.of("Any text"))
-            .id("singleton")
-            .build();
-        GetVacationResponse getVacationResponse = GetVacationResponse.builder()
-            .accountId(IDENTIFIER)
-            .vacationResponse(vacationResponse)
-            .build();
-
-        assertThat(getVacationResponse.getAccountId()).isEqualTo(IDENTIFIER);
-        assertThat(getVacationResponse.getList()).containsExactly(vacationResponse);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void getVacationResponseShouldThrowIfNoVacationResponse() {
-        GetVacationResponse.builder()
-            .accountId(IDENTIFIER)
-            .build();
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/HeaderTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/HeaderTest.java
deleted file mode 100644
index 3bb9d47..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/HeaderTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.Optional;
-
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class HeaderTest {
-
-    @Test
-    public void builderShouldThrowWhenHeaderIsNull() {
-        assertThatThrownBy(() -> Header.builder().header(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenHeaderIsEmpty() {
-        assertThatThrownBy(() -> Header.builder().header(ImmutableList.of())).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenHeaderHasMoreThanTwoElements() {
-        assertThatThrownBy(() -> Header.builder().header(ImmutableList.of("1", "2", "3"))).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void buildShouldThrowWhenNameIsNotGiven() {
-        assertThatThrownBy(() -> Header.builder().build()).isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void buildShouldSetNameWhenGiven() {
-        String expectedName = "name";
-        Header header = Header.builder().header(ImmutableList.of(expectedName)).build();
-
-        assertThat(header.getName()).isEqualTo(expectedName);
-        assertThat(header.getValue()).isEmpty();
-    }
-
-    @Test
-    public void buildShouldSetValueWhenGiven() {
-        String expectedName = "name";
-        String expectedValue = "value";
-        Header header = Header.builder().header(ImmutableList.of(expectedName, expectedValue)).build();
-
-        assertThat(header.getName()).isEqualTo(expectedName);
-        assertThat(header.getValue()).isEqualTo(Optional.of(expectedValue));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationRequestTest.java
deleted file mode 100644
index 4a302de..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationRequestTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.IOException;
-
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.MethodCallId;
-import org.junit.Test;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class InvocationRequestTest {
-
-    @Test(expected = IllegalStateException.class)
-    public void deserializedRequestsShouldThrowWhenNotEnoughElements() throws Exception {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getAccounts"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{}")};
-
-        InvocationRequest.deserialize(nodes);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void deserializedRequestsShouldThrowWhenTooMuchElements() throws Exception {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getAccounts"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{}"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#0"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("tooMuch")};
-
-        InvocationRequest.deserialize(nodes);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void deserializedRequestsShouldThrowWhenFirstParameterIsNotString() throws JsonParseException, JsonMappingException, IOException {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).booleanNode(true),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{}"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#0")};
-
-        InvocationRequest.deserialize(nodes);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void deserializedRequestsShouldThrowWhenSecondParameterIsNotJson() throws JsonParseException, JsonMappingException, IOException {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getAccounts"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("true"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#0")};
-
-        InvocationRequest.deserialize(nodes);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void deserializedRequestsShouldThrowWhenThirdParameterIsNotString() throws JsonParseException, JsonMappingException, IOException {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getAccounts"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{}"),
-                new ObjectNode(new JsonNodeFactory(false)).booleanNode(true)};
-
-        InvocationRequest.deserialize(nodes);
-    }
-
-    @Test
-    public void deserializedRequestsShouldWorkWhenSingleRequest() throws JsonParseException, JsonMappingException, IOException {
-        JsonNode[] nodes = new JsonNode[] { new ObjectNode(new JsonNodeFactory(false)).textNode("getAccounts"),
-                new ObjectNode(new JsonNodeFactory(false)).putObject("{\"id\": \"id\"}"),
-                new ObjectNode(new JsonNodeFactory(false)).textNode("#1")};
-
-        InvocationRequest request = InvocationRequest.deserialize(nodes);
-
-        assertThat(request.getMethodName()).isEqualTo(Method.Request.name("getAccounts"));
-        assertThat(request.getParameters()).isNotNull();
-        assertThat(request.getMethodCallId()).isEqualTo(MethodCallId.of("#1"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationResponseTest.java
deleted file mode 100644
index f293f12..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/InvocationResponseTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class InvocationResponseTest {
-
-    @Test(expected = NullPointerException.class)
-    public void newInstanceShouldThrowWhenMethodIsNull() {
-        new InvocationResponse(null, new ObjectNode(JsonNodeFactory.instance), MethodCallId.of("id"));
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void newInstanceShouldThrowWhenMethodIsEmpty() {
-        new InvocationResponse(Method.Response.name(""), new ObjectNode(JsonNodeFactory.instance), MethodCallId.of("id"));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void newInstanceShouldThrowWhenResultsIsNull() {
-        new InvocationResponse(Method.Response.name("method"), null, MethodCallId.of("id"));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void newInstanceShouldThrowWhenMethodCallIdIsNull() {
-        new InvocationResponse(Method.Response.name("method"), new ObjectNode(new JsonNodeFactory(false)).putObject("{}"), null);
-    }
-
-    @Test
-    public void asProtocolSpecificationShouldReturnAnArrayWithThreeElements() {
-        Object[] asProtocolSpecification = new InvocationResponse(Method.Response.name("method"), new ObjectNode(new JsonNodeFactory(false)).putObject("{}"), MethodCallId.of("#1"))
-                .asProtocolSpecification();
-
-        assertThat(asProtocolSpecification).hasSize(3);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/JmapMDNTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/JmapMDNTest.java
deleted file mode 100644
index 5e2912e..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/JmapMDNTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.nio.charset.StandardCharsets;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.InvalidOriginMessageForMDNException;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.mdn.action.mode.DispositionActionMode;
-import org.apache.james.mdn.sending.mode.DispositionSendingMode;
-import org.apache.james.mdn.type.DispositionType;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.address.Mailbox;
-import org.apache.james.mime4j.stream.RawField;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class JmapMDNTest {
-
-    public static final String TEXT_BODY = "text body";
-    public static final String SUBJECT = "subject";
-    public static final String REPORTING_UA = "reportingUA";
-    public static final TestMessageId MESSAGE_ID = TestMessageId.of(45);
-    public static final MDNDisposition DISPOSITION = MDNDisposition.builder()
-        .actionMode(DispositionActionMode.Automatic)
-        .sendingMode(DispositionSendingMode.Automatic)
-        .type(DispositionType.Processed)
-        .build();
-    public static final JmapMDN MDN = JmapMDN.builder()
-        .disposition(DISPOSITION)
-        .messageId(MESSAGE_ID)
-        .reportingUA(REPORTING_UA)
-        .subject(SUBJECT)
-        .textBody(TEXT_BODY)
-        .build();
-    public static final MailboxSession MAILBOX_SESSION = MailboxSessionUtil.create(Username.of("user@localhost.com"));
-
-    @Test
-    public void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(JmapMDN.class)
-            .verify();
-    }
-
-    @Test
-    public void builderShouldReturnObjectWhenAllFieldsAreValid() {
-        assertThat(MDN)
-            .isEqualTo(new JmapMDN(MESSAGE_ID, SUBJECT, TEXT_BODY, REPORTING_UA, DISPOSITION));
-    }
-
-    @Test
-    public void dispositionIsCompulsory() {
-        assertThatThrownBy(() ->
-            JmapMDN.builder()
-                .messageId(MESSAGE_ID)
-                .reportingUA(REPORTING_UA)
-                .subject(SUBJECT)
-                .textBody(TEXT_BODY)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void messageIdIsCompulsory() {
-        assertThatThrownBy(() ->
-            JmapMDN.builder()
-                .disposition(DISPOSITION)
-                .reportingUA(REPORTING_UA)
-                .subject(SUBJECT)
-                .textBody(TEXT_BODY)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void reportingUAIsCompulsory() {
-        assertThatThrownBy(() ->
-            JmapMDN.builder()
-                .disposition(DISPOSITION)
-                .messageId(MESSAGE_ID)
-                .subject(SUBJECT)
-                .textBody(TEXT_BODY)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void subjectIsCompulsory() {
-        assertThatThrownBy(() ->
-            JmapMDN.builder()
-                .disposition(DISPOSITION)
-                .messageId(MESSAGE_ID)
-                .reportingUA(REPORTING_UA)
-                .textBody(TEXT_BODY)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void textBodyIsCompulsory() {
-        assertThatThrownBy(() ->
-            JmapMDN.builder()
-                .disposition(DISPOSITION)
-                .messageId(MESSAGE_ID)
-                .reportingUA(REPORTING_UA)
-                .subject(SUBJECT)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void generateMDNMessageShouldUseDispositionHeaders() throws Exception {
-        String senderAddress = "sender@local";
-        Message originMessage = Message.Builder.of()
-            .setMessageId("45554@local.com")
-            .setFrom(senderAddress)
-            .setBody("body", StandardCharsets.UTF_8)
-            .addField(new RawField(JmapMDN.RETURN_PATH, "<" + senderAddress + ">"))
-            .addField(new RawField(JmapMDN.DISPOSITION_NOTIFICATION_TO, "<" + senderAddress + ">"))
-            .build();
-
-        assertThat(
-            MDN.generateMDNMessage(originMessage, MAILBOX_SESSION)
-                .getTo())
-            .extracting(address -> (Mailbox) address)
-            .extracting(Mailbox::getAddress)
-            .containsExactly(senderAddress);
-    }
-
-    @Test
-    public void generateMDNMessageShouldPositionDateHeader() throws Exception {
-        String senderAddress = "sender@local";
-        Message originMessage = Message.Builder.of()
-            .setMessageId("45554@local.com")
-            .setFrom(senderAddress)
-            .setBody("body", StandardCharsets.UTF_8)
-            .addField(new RawField(JmapMDN.RETURN_PATH, "<" + senderAddress + ">"))
-            .addField(new RawField(JmapMDN.DISPOSITION_NOTIFICATION_TO, "<" + senderAddress + ">"))
-            .build();
-
-        assertThat(
-            MDN.generateMDNMessage(originMessage, MAILBOX_SESSION)
-                .getDate())
-            .isNotNull();
-    }
-
-    @Test
-    public void generateMDNMessageShouldFailOnMissingDisposition() throws Exception {
-        String senderAddress = "sender@local";
-        Message originMessage = Message.Builder.of()
-            .setMessageId("45554@local.com")
-            .setFrom(senderAddress)
-            .setBody("body", StandardCharsets.UTF_8)
-            .addField(new RawField(JmapMDN.RETURN_PATH, "<" + senderAddress + ">"))
-            .build();
-
-        assertThatThrownBy(() ->
-            MDN.generateMDNMessage(originMessage, MAILBOX_SESSION))
-            .isInstanceOf(InvalidOriginMessageForMDNException.class);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MDNDispositionTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MDNDispositionTest.java
deleted file mode 100644
index 20f5a98..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MDNDispositionTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.mdn.action.mode.DispositionActionMode;
-import org.apache.james.mdn.sending.mode.DispositionSendingMode;
-import org.apache.james.mdn.type.DispositionType;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class MDNDispositionTest {
-
-    @Test
-    public void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(MDNDisposition.class)
-            .verify();
-    }
-
-    @Test
-    public void builderShouldReturnObjectWhenAllFieldsAreValid() {
-        assertThat(
-            MDNDisposition.builder()
-                .actionMode(DispositionActionMode.Automatic)
-                .sendingMode(DispositionSendingMode.Automatic)
-                .type(DispositionType.Processed)
-                .build())
-            .isEqualTo(new MDNDisposition(DispositionActionMode.Automatic,
-                DispositionSendingMode.Automatic,
-                DispositionType.Processed));
-    }
-
-    @Test
-    public void actionModeIsCompulsory() {
-        assertThatThrownBy(() ->
-            MDNDisposition.builder()
-                .sendingMode(DispositionSendingMode.Automatic)
-                .type(DispositionType.Processed)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void sendingModeIsCompulsory() {
-        assertThatThrownBy(() ->
-            MDNDisposition.builder()
-                .actionMode(DispositionActionMode.Automatic)
-                .type(DispositionType.Processed)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void typeIsCompulsory() {
-        assertThatThrownBy(() ->
-            MDNDisposition.builder()
-                .actionMode(DispositionActionMode.Automatic)
-                .sendingMode(DispositionSendingMode.Automatic)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java
deleted file mode 100644
index a0c9140..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.apache.james.mailbox.manager.ManagerTestProvisionner.OTHER_USER;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-
-import java.util.Optional;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.model.mailbox.MailboxNamespace;
-import org.apache.james.jmap.model.mailbox.Rights;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.manager.ManagerTestProvisionner;
-import org.apache.james.mailbox.model.MailboxACL;
-import org.apache.james.mailbox.model.MailboxCounters;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxMetaData;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.UidValidity;
-import org.apache.james.mailbox.model.search.MailboxQuery;
-import org.apache.james.mailbox.quota.QuotaManager;
-import org.apache.james.mailbox.quota.QuotaRootResolver;
-import org.apache.james.mailbox.store.StoreMailboxManager;
-import org.assertj.core.api.JUnitSoftAssertions;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import org.apache.james.jmap.model.Number;
-
-public class MailboxFactoryTest {
-    public static final char DELIMITER = '.';
-
-    @Rule
-    public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
-
-    private StoreMailboxManager mailboxManager;
-    private MailboxSession mailboxSession;
-    private MailboxSession otherMailboxSession;
-    private Username user;
-    private Username otherUser;
-    private MailboxFactory sut;
-
-    @Before
-    public void setup() throws Exception {
-        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
-        QuotaRootResolver quotaRootResolver = mailboxManager.getQuotaComponents().getQuotaRootResolver();
-        QuotaManager quotaManager = mailboxManager.getQuotaComponents().getQuotaManager();
-
-        user = ManagerTestProvisionner.USER;
-        otherUser = OTHER_USER;
-        mailboxSession = mailboxManager.authenticate(user, ManagerTestProvisionner.USER_PASS).withoutDelegation();
-        otherMailboxSession = mailboxManager.authenticate(otherUser, ManagerTestProvisionner.OTHER_USER_PASS).withoutDelegation();
-        sut = new MailboxFactory(mailboxManager, quotaManager, quotaRootResolver);
-    }
-
-
-    @Test
-    public void mailboxFromMailboxIdShouldReturnAbsentWhenDoesntExist() throws Exception {
-        Optional<Mailbox> mailbox = sut.builder()
-                .id(InMemoryId.of(123))
-                .session(mailboxSession)
-                .build()
-                .blockOptional();
-
-        assertThat(mailbox).isEmpty();
-    }
-
-    @Test
-    public void mailboxFromMailboxIdShouldReturnPresentWhenExists() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "myBox");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        MailboxId mailboxId = mailboxManager.getMailbox(mailboxPath, mailboxSession).getId();
-
-        Optional<Mailbox> mailbox = sut.builder()
-                .id(mailboxId)
-                .session(mailboxSession)
-                .build()
-                .blockOptional();
-
-        assertThat(mailbox).isPresent();
-        assertThat(mailbox.get().getId()).isEqualTo(mailboxId);
-    }
-
-    @Test
-    public void getNameShouldReturnMailboxNameWhenRootMailbox() throws Exception {
-        String expected = "mailbox";
-        MailboxPath mailboxPath = MailboxPath.forUser(user, expected);
-
-        String name = sut.getName(mailboxPath, mailboxSession);
-        assertThat(name).isEqualTo(expected);
-    }
-
-    @Test
-    public void getNameShouldReturnMailboxNameWhenChildMailbox() throws Exception {
-        String expected = "mailbox";
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "inbox." + expected);
-
-        String name = sut.getName(mailboxPath, mailboxSession);
-        assertThat(name).isEqualTo(expected);
-    }
-
-    @Test
-    public void getNameShouldReturnMailboxNameWhenChildOfChildMailbox() throws Exception {
-        String expected = "mailbox";
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "inbox.children." + expected);
-
-        String name = sut.getName(mailboxPath, mailboxSession);
-        assertThat(name).isEqualTo(expected);
-    }
-
-    @Test
-    public void getParentIdFromMailboxPathShouldReturNullWhenRootMailbox() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "mailbox");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        Optional<MailboxId> id = sut.getParentIdFromMailboxPath(mailboxPath, Optional.empty(), mailboxSession).block();
-        assertThat(id).isEmpty();
-    }
-
-    @Test
-    public void getParentIdFromMailboxPathShouldReturnParentIdWhenChildMailbox() throws Exception {
-        MailboxPath parentMailboxPath = MailboxPath.inbox(user);
-        mailboxManager.createMailbox(parentMailboxPath, mailboxSession);
-        MailboxId parentId = mailboxManager.getMailbox(parentMailboxPath, mailboxSession).getId();
-
-        MailboxPath mailboxPath = parentMailboxPath.child("mailbox", '.');
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        Optional<MailboxId> id = sut.getParentIdFromMailboxPath(mailboxPath, Optional.empty(), mailboxSession).block();
-        assertThat(id).contains(parentId);
-    }
-
-    @Test
-    public void getParentIdFromMailboxPathShouldReturnParentIdWhenChildOfChildMailbox() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "inbox.children.mailbox");
-        mailboxManager.createMailbox(MailboxPath.forUser(user, "inbox"), mailboxSession);
-
-        MailboxPath parentMailboxPath = MailboxPath.forUser(user, "inbox.children");
-        mailboxManager.createMailbox(parentMailboxPath, mailboxSession);
-        MailboxId parentId = mailboxManager.getMailbox(parentMailboxPath, mailboxSession).getId();
-
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        Optional<MailboxId> id = sut.getParentIdFromMailboxPath(mailboxPath, Optional.empty(), mailboxSession).block();
-        assertThat(id).contains(parentId);
-    }
-
-    @Test
-    public void getParentIdFromMailboxPathShouldWorkWhenUserMailboxesProvided() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "inbox.children.mailbox");
-        mailboxManager.createMailbox(MailboxPath.forUser(user, "inbox"), mailboxSession);
-
-        MailboxPath parentMailboxPath = MailboxPath.forUser(user, "inbox.children");
-        mailboxManager.createMailbox(parentMailboxPath, mailboxSession);
-        MailboxId parentId = mailboxManager.getMailbox(parentMailboxPath, mailboxSession).getId();
-
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        org.apache.james.mailbox.model.Mailbox mailbox = new org.apache.james.mailbox.model.Mailbox(parentMailboxPath, UidValidity.of(34), parentId);
-        Optional<MailboxId> id = sut.getParentIdFromMailboxPath(mailboxPath,
-            Optional.of(ImmutableMap.of(parentMailboxPath, new MailboxMetaData(mailbox, DELIMITER,
-                MailboxMetaData.Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, new MailboxACL(),
-                MailboxCounters.empty(parentId)))),
-            mailboxSession).block();
-        assertThat(id).contains(parentId);
-    }
-
-    @Test
-    public void getNamespaceShouldReturnPersonalNamespaceWhenUserMailboxPathAndUserMailboxSessionAreTheSame() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(mailboxSession)
-            .build()
-            .block();
-
-        assertThat(retrievedMailbox.getNamespace())
-            .isEqualTo(MailboxNamespace.personal());
-    }
-
-    @Test
-    public void buildShouldRelyOnPreloadedMailboxes() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> otherId = mailboxManager.createMailbox(inbox.child("child", '.'), mailboxSession);
-
-        InMemoryId preLoadedId = InMemoryId.of(45);
-        final org.apache.james.mailbox.model.Mailbox mailbox = new org.apache.james.mailbox.model.Mailbox(inbox, UidValidity.of(34), preLoadedId);
-        Mailbox retrievedMailbox = sut.builder()
-            .id(otherId.get())
-            .session(mailboxSession)
-            .usingPreloadedMailboxesMetadata(Optional.of(ImmutableMap.of(inbox, new MailboxMetaData(
-                mailbox,
-                DELIMITER,
-                MailboxMetaData.Children.NO_INFERIORS,
-                MailboxMetaData.Selectability.NONE,
-                MailboxACL.EMPTY,
-                MailboxCounters.empty(preLoadedId)))))
-            .build()
-            .block();
-
-        assertThat(retrievedMailbox.getParentId())
-            .contains(preLoadedId);
-    }
-
-    @Test
-    public void getNamespaceShouldReturnDelegatedNamespaceWhenUserMailboxPathAndUserMailboxSessionAreNotTheSame() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-        mailboxManager.applyRightsCommand(inbox,
-            MailboxACL.command()
-                .forUser(otherUser)
-                .rights(MailboxACL.Right.Read, MailboxACL.Right.Lookup)
-                .asAddition(),
-            mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(otherMailboxSession)
-            .build()
-            .block();
-
-        assertThat(retrievedMailbox.getNamespace())
-            .isEqualTo(MailboxNamespace.delegated(user));
-    }
-
-    @Test
-    public void ownerShouldHaveFullRightsViaMayProperties() throws Exception {
-        MailboxPath inbox = MailboxPath.forUser(user, "inbox");
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(mailboxSession)
-            .build()
-            .block();
-
-        softly.assertThat(retrievedMailbox.isMayAddItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayCreateChild()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayDelete()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayReadItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayRemoveItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayRename()).isTrue();
-    }
-
-    @Test
-    public void delegatedUserShouldHaveMayAddItemsWhenAllowedToInsert() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-        mailboxManager.applyRightsCommand(inbox,
-            MailboxACL.command()
-                .forUser(otherUser)
-                .rights(MailboxACL.Right.Insert, MailboxACL.Right.Lookup)
-                .asAddition(),
-            mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(otherMailboxSession)
-            .build()
-            .block();
-
-        softly.assertThat(retrievedMailbox.isMayAddItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayCreateChild()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayDelete()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayReadItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayRemoveItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayRename()).isFalse();
-    }
-
-    @Test
-    public void delegatedUserShouldHaveMayReadItemsWhenAllowedToRead() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-        mailboxManager.applyRightsCommand(inbox,
-            MailboxACL.command()
-                .forUser(otherUser)
-                .rights(MailboxACL.Right.Read, MailboxACL.Right.Lookup)
-                .asAddition(),
-            mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(otherMailboxSession)
-            .build()
-            .block();
-
-        softly.assertThat(retrievedMailbox.isMayAddItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayCreateChild()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayDelete()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayReadItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayRemoveItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayRename()).isFalse();
-    }
-
-    @Test
-    public void delegatedUserShouldHaveMayRemoveItemsWhenAllowedToRemoveItems() throws Exception {
-        MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(inbox, mailboxSession);
-        mailboxManager.applyRightsCommand(inbox,
-            MailboxACL.command()
-                .forUser(otherUser)
-                .rights(MailboxACL.Right.DeleteMessages, MailboxACL.Right.Lookup)
-                .asAddition(),
-            mailboxSession);
-
-        Mailbox retrievedMailbox = sut.builder()
-            .id(mailboxId.get())
-            .session(otherMailboxSession)
-            .build()
-            .block();
-
-        softly.assertThat(retrievedMailbox.isMayAddItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayCreateChild()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayDelete()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayReadItems()).isFalse();
-        softly.assertThat(retrievedMailbox.isMayRemoveItems()).isTrue();
-        softly.assertThat(retrievedMailbox.isMayRename()).isFalse();
-    }
-
-    @Test
-    public void mailboxFromMetaDataShouldReturnPresentStoredValue() throws Exception {
-        String name = "myBox";
-        MailboxPath mailboxPath = MailboxPath.forUser(user, name);
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        mailboxManager.setRights(mailboxPath, MailboxACL.EMPTY.apply(MailboxACL.command()
-                .forUser(OTHER_USER)
-                .rights(MailboxACL.Right.Lookup, MailboxACL.Right.Read)
-                .asAddition()),
-            mailboxSession);
-        MailboxMetaData metaData = mailboxManager.search(MailboxQuery.privateMailboxesBuilder(mailboxSession).build(), mailboxSession)
-            .toStream()
-            .filter(metadata -> metadata.getPath().equals(mailboxPath))
-            .findFirst()
-            .get();
-
-        Optional<Mailbox> mailbox = sut.builder()
-            .mailboxMetadata(metaData)
-            .session(mailboxSession)
-            .build()
-            .blockOptional();
-
-        softly.assertThat(mailbox).isPresent();
-        softly.assertThat(mailbox).map(Mailbox::getId).contains(metaData.getId());
-        softly.assertThat(mailbox).map(Mailbox::getName).contains(name);
-        softly.assertThat(mailbox).map(Mailbox::getTotalMessages).contains(Number.ZERO);
-        softly.assertThat(mailbox).map(Mailbox::getUnreadMessages).contains(Number.ZERO);
-        softly.assertThat(mailbox).map(Mailbox::getSharedWith).contains(new Rights(ImmutableMap.of(
-            OTHER_USER, ImmutableList.of(Rights.Right.Lookup, Rights.Right.Read))));
-    }
-
-    @Test
-    public void buildShouldThrowWhenBothMetadataAndId() {
-        assertThatThrownBy(() ->
-            sut.builder()
-                .session(mailboxSession)
-                .id(mock(MailboxId.class))
-                .mailboxMetadata(mock(MailboxMetaData.class))
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void buildShouldThrowWhenNoId() {
-        assertThatThrownBy(() ->
-            sut.builder()
-                .session(mailboxSession)
-                .build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxPropertyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxPropertyTest.java
deleted file mode 100644
index f9cbc2a..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxPropertyTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.Optional;
-
-import org.junit.Test;
-
-public class MailboxPropertyTest {
-
-    @Test
-    public void findPropertyShouldReturnEmptyWhenNoEnumEntryMatchGivenString() {
-        assertThat(MailboxProperty.findProperty("should not match any entry")).isEmpty();
-    }
-
-    @Test
-    public void findPropertyShouldThrowWhenNullString() {
-        assertThatThrownBy(() -> MailboxProperty.findProperty(null))
-            .isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void findPropertyShouldReturnMatchingEnumEntryWhenExistingValue() {
-        assertThat(MailboxProperty.findProperty("name")).isEqualTo(Optional.of(MailboxProperty.NAME));
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessageHeaderPropertyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessageHeaderPropertyTest.java
deleted file mode 100644
index 621c383..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessageHeaderPropertyTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
-import org.junit.Test;
-
-public class MessageHeaderPropertyTest {
-
-    @Test
-    public void fromFieldNameShouldLowercaseFieldName() {
-        assertThat(HeaderProperty.fromFieldName("FiElD")).isEqualTo(HeaderProperty.fromFieldName("field"));
-    }
-
-    @Test
-    public void fromFieldNameShouldThrowWhenStartWithHeaderPrefix() {
-        assertThatThrownBy(() -> HeaderProperty.fromFieldName("headers.FiElD")).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void valueOfShouldThrowWhenNull() {
-        assertThatThrownBy(() -> HeaderProperty.valueOf(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void valueOfalueOfShouldThrowWhenNull() {
-        assertThatThrownBy(() -> HeaderProperty.valueOf(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void valueOfShouldReturnLowerCasedProperty() {
-        HeaderProperty headerProperty = HeaderProperty.valueOf("headers.ProP");
-
-        assertThat(headerProperty.asFieldName()).isEqualTo("prop");
-    }
-
-    @Test
-    public void valueOfShouldThrowWhenValueIsNotHeader() {
-        assertThatThrownBy(() -> HeaderProperty.valueOf("ProP")).isInstanceOf(IllegalArgumentException.class);
-    }
-    
-    @Test
-    public void findShouldReturnStreamWhenValueStartsWithRightString() {
-        assertThat(HeaderProperty.find(HeaderProperty.HEADER_PROPERTY_PREFIX + "myvalue"))
-            .contains(HeaderProperty.valueOf(HeaderProperty.HEADER_PROPERTY_PREFIX + "myvalue"));
-    }
-
-    @Test
-    public void findShouldReturnEmptyStreamWhenValueStartsWithWrongString() {
-        assertThat(HeaderProperty.find("bad value" + HeaderProperty.HEADER_PROPERTY_PREFIX + "myvalue")).isEmpty();
-    }
-    
-    @Test
-    public void equalsShouldBeTrueWhenIdenticalProperties() {
-        assertThat(HeaderProperty.valueOf("headers.prop")).isEqualTo(HeaderProperty.valueOf("headers.prop"));
-    }
-
-    @Test
-    public void equalsShouldBeFalseWhenDifferentProperties() {
-        assertThat(HeaderProperty.valueOf("headers.prop")).isNotEqualTo(HeaderProperty.valueOf("headers.other"));
-    }
-
-    @Test
-    public void equalsShouldBeTrueWhenDifferentCaseProperties() {
-        assertThat(HeaderProperty.valueOf("headers.prOP")).isEqualTo(HeaderProperty.valueOf("headers.PRop"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertiesTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertiesTest.java
deleted file mode 100644
index 67b4501..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertiesTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.model.MessageProperties;
-import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import com.google.common.collect.ImmutableSet;
-
-class MessagePropertiesTest {
-
-    @Test
-    void toOutputPropertiesShouldReturnAllMessagePropertiesWhenAbsent() {
-        MessageProperties actual = new MessageProperties(Optional.empty()).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties()).hasValue(MessageProperty.allOutputProperties());
-    }
-    
-    @Test
-    void toOutputPropertiesShouldReturnEmptyHeaderPropertiesWhenAbsent() {
-        MessageProperties actual = new MessageProperties(Optional.empty()).toOutputProperties();
-        assertThat(actual.getOptionalHeadersProperties()).isEmpty();
-    }
-
-    @Test
-    void toOutputPropertiesShouldReturnTextBodyWhenBodyRequested() {
-        MessageProperties actual = new MessageProperties(Optional.of(ImmutableSet.of("body"))).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties())
-            .hasValueSatisfying(value -> 
-                assertThat(value).contains(MessageProperty.textBody).doesNotContain(MessageProperty.body));
-    }
-
-    @Test
-    void toOutputPropertiesShouldReturnIsUnread() {
-        MessageProperties actual = new MessageProperties(Optional.of(ImmutableSet.of("isUnread"))).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties())
-                .hasValueSatisfying(value ->
-                        assertThat(value).contains(MessageProperty.isUnread));
-    }
-
-    @Test
-    void toOutputPropertiesShouldReturnMandatoryPropertiesWhenEmptyRequest() {
-        MessageProperties actual = new MessageProperties(Optional.of(ImmutableSet.of())).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties())
-            .hasValue(ImmutableSet.of(MessageProperty.id));
-    }
-
-    @Test
-    void toOutputPropertiesShouldReturnAllHeadersWhenHeadersAndIndividualHeadersRequested() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("headers.X-Spam-Score", "headers"))).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties()).hasValueSatisfying(
-            value -> assertThat(value).contains(MessageProperty.headers)
-        );
-        assertThat(actual.getOptionalHeadersProperties()).isEmpty();
-    }
-
-    @Test
-    void toOutputPropertiesShouldReturnHeadersMessagePropertyWhenIndividualHeadersRequested() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("headers.X-Spam-Score"))).toOutputProperties();
-        assertThat(actual.getOptionalMessageProperties()).hasValueSatisfying(
-            value -> assertThat(value).contains(MessageProperty.headers)
-        );
-        assertThat(actual.getOptionalHeadersProperties()).hasValueSatisfying(
-            value -> assertThat(value).contains(HeaderProperty.fromFieldName("x-spam-score"))
-        );
-    }
-
-    @Test
-    void computeReadLevelShouldReturnHeaderWhenOnlyHeaderProperties() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("headers.X-Spam-Score"))).toOutputProperties();
-
-        assertThat(actual.computeReadLevel())
-            .isEqualTo(MessageProperties.ReadProfile.Header);
-    }
-
-    @Test
-    void computeReadLevelShouldReturnMetadataWhenOnlyKeywordProperty() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("keywords"))).toOutputProperties();
-
-        assertThat(actual.computeReadLevel())
-            .isEqualTo(MessageProperties.ReadProfile.Metadata);
-    }
-
-    @Test
-    void computeReadLevelShouldReturnFullWhenHtmlBodyProperty() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("htmlBody"))).toOutputProperties();
-
-        assertThat(actual.computeReadLevel())
-            .isEqualTo(MessageProperties.ReadProfile.Full);
-    }
-
-    @Test
-    void computeReadLevelShouldCombineReadLevels() {
-        MessageProperties actual = new MessageProperties(
-            Optional.of(ImmutableSet.of("headers.X-Spam-Score", "keywords"))).toOutputProperties();
-
-        assertThat(actual.computeReadLevel())
-            .isEqualTo(MessageProperties.ReadProfile.Header);
-    }
-
-    @Nested
-    class ReadProfileTest {
-        @Test
-        void combineShouldReturnMetadataWhenOnlyMetadata() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                    MessageProperties.ReadProfile.Metadata,
-                    MessageProperties.ReadProfile.Metadata))
-                .isEqualTo(MessageProperties.ReadProfile.Metadata);
-        }
-
-        @Test
-        void combineShouldReturnHeaderWhenOnlyHeader() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                    MessageProperties.ReadProfile.Header,
-                    MessageProperties.ReadProfile.Header))
-                .isEqualTo(MessageProperties.ReadProfile.Header);
-        }
-
-        @Test
-        void combineShouldReturnFullWhenOnlyFull() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                    MessageProperties.ReadProfile.Full,
-                    MessageProperties.ReadProfile.Full))
-                .isEqualTo(MessageProperties.ReadProfile.Full);
-        }
-
-        @Test
-        void combineShouldReturnFastWhenOnlyFast() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                    MessageProperties.ReadProfile.Fast,
-                    MessageProperties.ReadProfile.Fast))
-                .isEqualTo(MessageProperties.ReadProfile.Fast);
-        }
-
-        @Test
-        void combineShouldReturnHeaderWhenHeaderAndMetadata() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                MessageProperties.ReadProfile.Metadata,
-                MessageProperties.ReadProfile.Header))
-                .isEqualTo(MessageProperties.ReadProfile.Header);
-        }
-
-        @Test
-        void combineShouldReturnFullWhenFullAndMetadata() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                MessageProperties.ReadProfile.Metadata,
-                MessageProperties.ReadProfile.Full))
-                .isEqualTo(MessageProperties.ReadProfile.Full);
-        }
-
-        @Test
-        void combineShouldReturnFastWhenFastAndHeader() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                MessageProperties.ReadProfile.Header,
-                MessageProperties.ReadProfile.Fast))
-                .isEqualTo(MessageProperties.ReadProfile.Fast);
-        }
-
-        @Test
-        void combineShouldReturnFullWhenFullAndFast() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                MessageProperties.ReadProfile.Fast,
-                MessageProperties.ReadProfile.Full))
-                .isEqualTo(MessageProperties.ReadProfile.Full);
-        }
-
-        @Test
-        void combineShouldCommute() {
-            assertThat(MessageProperties.ReadProfile.combine(
-                MessageProperties.ReadProfile.Full,
-                MessageProperties.ReadProfile.Fast))
-                .isEqualTo(MessageProperties.ReadProfile.Full);
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertyTest.java
deleted file mode 100644
index 58f4982..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MessagePropertyTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import org.junit.Test;
-
-public class MessagePropertyTest {
-    
-    @Test
-    public void findShouldThrowWhenNull() {
-        assertThatThrownBy(() -> MessageProperty.find(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    
-    @Test
-    public void findShouldReturnEmptyWhenNotFound() {
-        assertThat(MessageProperty.find("not found")).isEmpty();
-    }
-    
-    @Test
-    public void findShouldReturnEnumEntryWhenFound() {
-        assertThat(MessageProperty.find("subject")).containsExactly(MessageProperty.subject);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/NumberTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/NumberTest.java
deleted file mode 100644
index e0956dc..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/NumberTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.junit.Test;
-import org.apache.james.jmap.model.Number;
-
-public class NumberTest {
-    @Test
-    public void fromOutboundLongShouldReturnMinValueWhenNegativeValue() throws Exception {
-        assertThat(Number.BOUND_SANITIZING_FACTORY.from(-1))
-            .isEqualTo(Number.ZERO);
-    }
-
-    @Test
-    public void fromOutboundLongShouldSanitizeTooBigNumbers() throws Exception {
-        assertThat(Number.BOUND_SANITIZING_FACTORY.from(Number.MAX_VALUE  + 1))
-            .isEqualTo(Number.fromLong(Number.MAX_VALUE));
-    }
-
-    @Test
-    public void fromLongShouldThrowWhenNegativeValue() throws Exception {
-        assertThatThrownBy(() ->
-            Number.fromLong(-1))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void fromLongShouldThrowWhenOver2Pow53Value() throws Exception {
-        assertThatThrownBy(() ->
-            Number.fromLong(Number.MAX_VALUE + 1))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void fromLongShouldReturnValueWhenZero() throws Exception {
-        assertThat(Number.fromLong(0).asLong())
-            .isEqualTo(0);
-    }
-
-    @Test
-    public void fromLongShouldReturnValueWhenMaxValue() throws Exception {
-        assertThat(Number.fromLong(Number.MAX_VALUE).asLong())
-            .isEqualTo(Number.MAX_VALUE);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/OldKeywordTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/OldKeywordTest.java
deleted file mode 100644
index 6e52664..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/OldKeywordTest.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE 2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import jakarta.mail.Flags;
-import jakarta.mail.Flags.Flag;
-
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.jmap.model.Keywords;
-import org.apache.james.mailbox.FlagsBuilder;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class OldKeywordTest {
-    @Test
-    public void shouldRespectBeanContract() {
-        EqualsVerifier.forClass(OldKeyword.class).verify();
-    }
-
-    @Test
-    public void asKeywordsShouldContainFlaggedWhenIsFlagged() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isFlagged(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from(Keyword.FLAGGED));
-    }
-
-    @Test
-    public void asKeywordsShouldNotContainFlaggedWhenIsNotFlagged() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isFlagged(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from());
-    }
-
-    @Test
-    public void asKeywordsShouldNotContainSeenWhenIsUnRead() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from());
-    }
-
-    @Test
-    public void asKeywordsShouldContainSeenWhenIsRead() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(false))
-            .computeOldKeyword();
-
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from(Keyword.SEEN));
-    }
-
-    @Test
-    public void asKeywordsShouldContainAnsweredWhenIsAnswered() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isAnswered(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from(Keyword.ANSWERED));
-    }
-
-    @Test
-    public void asKeywordsShouldNotContainAnsweredWhenIsNotAnswered() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isAnswered(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from());
-    }
-
-    @Test
-    public void asKeywordsShouldContainDraftWhenIsDraft() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isDraft(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from(Keyword.DRAFT));
-    }
-
-    @Test
-    public void asKeywordsShouldNotContainDraftWhenIsNotDraft() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isDraft(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from());
-    }
-
-    @Test
-    public void asKeywordsShouldContainForwardedWhenIsForwarded() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isForwarded(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from(Keyword.FORWARDED));
-    }
-
-    @Test
-    public void asKeywordsShouldNotContainForwardedWhenIsNotForwarded() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isForwarded(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().asKeywords())
-            .isEqualTo(Keywords.strictFactory().from());
-    }
-
-    @Test
-    public void computeOldKeywordsShouldReturnEmptyWhenAllEmpty() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .computeOldKeyword();
-
-        assertThat(testee).isEmpty();
-    }
-
-    @Test
-    public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isFlagged(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags())).isEqualTo(new Flags(Flag.FLAGGED));
-    }
-
-    @Test
-    public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isFlagged(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags(Flag.FLAGGED))).isEqualTo(new Flags());
-    }
-
-
-    @Test
-    public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(true))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags());
-    }
-
-    @Test
-    public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags(Flag.SEEN));
-    }
-
-    @Test
-    public void applyStateShouldPreserveRecentFlag() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags(Flag.RECENT)))
-            .isEqualTo(new FlagsBuilder().add(Flag.RECENT, Flag.SEEN).build());
-    }
-
-    @Test
-    public void applyStateShouldPreserveDeletedFlag() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(false))
-            .computeOldKeyword();
-
-        assertThat(testee.get().applyToState(new Flags(Flag.DELETED)))
-            .isEqualTo(new FlagsBuilder().add(Flag.DELETED, Flag.SEEN).build());
-    }
-
-    @Test
-    public void applyStateShouldPreserveCustomFlag() {
-        Optional<OldKeyword> testee = OldKeyword.builder()
-            .isUnread(Optional.of(false))
-            .computeOldKeyword();
-
-        String customFlag = "custom";
-        assertThat(testee.get().applyToState(new Flags(customFlag)))
-            .isEqualTo(new FlagsBuilder().add(Flag.SEEN).add(customFlag).build());
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetErrorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetErrorTest.java
deleted file mode 100644
index 038c3b5..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetErrorTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.apache.james.jmap.model.MessageProperties.MessageProperty;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.Optional;
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableSet;
-
-public class SetErrorTest {
-
-    @Test
-    public void buildShouldThrowWhenTypeIsNotGiven() {
-        assertThatThrownBy(() -> SetError.builder().build())
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void buildShouldWorkWhenAllMandatoryFieldsAreGiven() {
-        SetError expected = new SetError(SetError.Type.ERROR, Optional.empty(), Optional.empty());
-
-        SetError setError = SetError.builder()
-            .type(SetError.Type.ERROR)
-            .build();
-
-        assertThat(setError).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldWorkWhenAllFieldsAreGiven() {
-        ImmutableSet<MessageProperty> props = ImmutableSet.of(MessageProperty.attachedMessages);
-
-        SetError expected = new SetError(SetError.Type.ERROR, Optional.of("description"), Optional.of(props));
-
-        SetError setError = SetError.builder()
-                .type(SetError.Type.ERROR)
-                .description("description")
-                .properties(ImmutableSet.of(MessageProperty.attachedMessages))
-                .build();
-
-        assertThat(setError).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void buildShouldMergePassedProperties() {
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description")
-                .properties(ImmutableSet.of(MessageProperty.bcc))
-                .properties(ImmutableSet.of(MessageProperty.cc))
-                .build();
-
-        assertThat(result.getProperties()).contains(ImmutableSet.of(MessageProperty.bcc, MessageProperty.cc));
-    }
-
-    @Test
-    public void buildShouldDefaultToEmptyWhenPropertiesOmitted() {
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description")
-                .build();
-
-        assertThat(result.getProperties()).isEmpty();
-    }
-
-    @Test
-    public void buildShouldDefaultToEmptyWhenPropertiesNull() {
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description").properties((Set<MessageProperty>)null)
-                .build();
-
-        assertThat(result.getProperties()).isPresent();
-        assertThat(result.getProperties().get()).isEmpty();
-    }
-
-    @Test
-    public void buildShouldBeIdempotentWhenNullPropertiesSet() {
-        ImmutableSet<MessageProperty> nonNullProperty = ImmutableSet.of(MessageProperty.from);
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description")
-                .properties(nonNullProperty)
-                .properties((Set<MessageProperty>)null)
-                .build();
-
-        assertThat(result.getProperties()).isPresent();
-        assertThat(result.getProperties().get()).isEqualTo(nonNullProperty);
-    }
-
-    @Test
-    public void buildShouldDefaultToEmptyWhenPropertiesWithNoArgument() {
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description").properties()
-                .build();
-
-        assertThat(result.getProperties()).isPresent();
-        assertThat(result.getProperties().get()).isEmpty();
-    }
-
-    @Test
-    public void buildShouldBeIdempotentWhenPropertiesWithNoArgument() {
-        ImmutableSet<MessageProperty> nonNullProperty = ImmutableSet.of(MessageProperty.from);
-        SetError result = SetError.builder()
-                .type(SetError.Type.ERROR).description("a description")
-                .properties(nonNullProperty)
-                .properties()
-                .build();
-
-        assertThat(result.getProperties()).isPresent();
-        assertThat(result.getProperties().get()).isEqualTo(nonNullProperty);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesRequestTest.java
deleted file mode 100644
index c7cf039..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesRequestTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.draft.model.mailbox.MailboxCreateRequest;
-import org.apache.james.jmap.draft.model.mailbox.MailboxUpdateRequest;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMailboxesRequestTest {
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenAccountId() {
-        SetMailboxesRequest.builder().accountId("1");
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void builderShouldThrowWhenIfInState() {
-        SetMailboxesRequest.builder().ifInState("1");
-    }
-
-    @Test
-    public void builderShouldWork() throws MailboxException {
-        //Given
-        MailboxCreationId creationId = MailboxCreationId.of("creationId");
-        InMemoryId mailboxId = InMemoryId.of(123);
-        MailboxCreateRequest mailboxRequest = MailboxCreateRequest.builder()
-            .name("mailboxRequest")
-            .build();
-        ImmutableList<MailboxId> destroy = ImmutableList.of(InMemoryId.of(456));
-        MailboxUpdateRequest mailboxUpdateRequest = MailboxUpdateRequest.builder()
-            .name("mailboxUpdateRequest")
-            .build();
-        SetMailboxesRequest expected = new SetMailboxesRequest(ImmutableMap.of(creationId, mailboxRequest), ImmutableMap.of(mailboxId, mailboxUpdateRequest), destroy);
-
-        //When
-        SetMailboxesRequest actual = SetMailboxesRequest.builder()
-            .create(creationId, mailboxRequest)
-            .update(mailboxId, mailboxUpdateRequest)
-            .destroy(destroy)
-            .build();
-
-        //Then
-        assertThat(actual).isEqualToComparingFieldByField(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesResponseTest.java
deleted file mode 100644
index c84ce34..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMailboxesResponseTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMailboxesResponseTest {
-
-    @Test
-    public void builderShouldWork() {
-        ImmutableMap<MailboxCreationId, Mailbox> created = ImmutableMap.of(MailboxCreationId.of("1"),
-            Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("myBox")
-                .build());
-        ImmutableList<MailboxId> updated = ImmutableList.of(InMemoryId.of(2));
-        ImmutableList<MailboxId> destroyed = ImmutableList.of(InMemoryId.of(3));
-        ImmutableMap<MailboxCreationId, SetError> notCreated = ImmutableMap.of(MailboxCreationId.of("dead-beef-defec8"), SetError.builder().type(SetError.Type.INVALID_PROPERTIES).build());
-        ImmutableMap<MailboxId, SetError> notUpdated = ImmutableMap.of(InMemoryId.of(4), SetError.builder().type(SetError.Type.INVALID_ARGUMENTS).build());
-        ImmutableMap<MailboxId, SetError> notDestroyed  = ImmutableMap.of(InMemoryId.of(5), SetError.builder().type(SetError.Type.NOT_FOUND).build());
-        SetMailboxesResponse expected = new SetMailboxesResponse(created, updated, destroyed, notCreated, notUpdated, notDestroyed);
-
-        SetMailboxesResponse setMessagesResponse = SetMailboxesResponse.builder()
-            .created(created)
-            .updated(updated)
-            .destroyed(destroyed)
-            .notCreated(notCreated)
-            .notUpdated(notUpdated)
-            .notDestroyed(notDestroyed)
-            .build();
-
-        assertThat(setMessagesResponse).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void mergeIntoShouldCopyItemsWhenBuilderIsEmpty() {
-        // Given
-        SetMailboxesResponse.Builder emptyBuilder = SetMailboxesResponse.builder();
-        SetMailboxesResponse testee = SetMailboxesResponse.builder()
-                .created(buildMailbox(MailboxCreationId.of("1")))
-                .destroyed(InMemoryId.of(2))
-                .notCreated(ImmutableMap.of(MailboxCreationId.of("dead-beef-defec8"), SetError.builder().type(SetError.Type.INVALID_PROPERTIES).build()))
-                .notDestroyed(ImmutableMap.of(InMemoryId.of(3), SetError.builder().type(SetError.Type.INVALID_PROPERTIES).build()))
-                .build();
-
-        // When
-        testee.mergeInto(emptyBuilder);
-        // Then
-        assertThat(emptyBuilder.build()).isEqualToComparingFieldByField(testee);
-    }
-
-    private ImmutableMap<MailboxCreationId, Mailbox> buildMailbox(MailboxCreationId mailboxId) {
-        return ImmutableMap.of(mailboxId, Mailbox.builder()
-                .id(InMemoryId.of(Long.parseLong(mailboxId.getCreationId())))
-                .name(mailboxId.getCreationId())
-                .build());
-    }
-
-    @Test
-    public void mergeIntoShouldMergeCreatedLists() {
-        // Given
-        MailboxCreationId buildersCreatedMessageId = MailboxCreationId.of("1");
-        SetMailboxesResponse.Builder nonEmptyBuilder = SetMailboxesResponse.builder()
-                .created(buildMailbox(buildersCreatedMessageId));
-        MailboxCreationId createdMessageId = MailboxCreationId.of("2");
-        SetMailboxesResponse testee = SetMailboxesResponse.builder()
-                .created(buildMailbox(createdMessageId))
-                .build();
-        // When
-        testee.mergeInto(nonEmptyBuilder);
-        SetMailboxesResponse mergedResponse = nonEmptyBuilder.build();
-
-        // Then
-        assertThat(mergedResponse.getCreated().keySet()).containsExactly(buildersCreatedMessageId, createdMessageId);
-    }
-
-    @Test
-    public void mergeIntoShouldMergeDestroyedLists() {
-        // Given
-        InMemoryId buildersDestroyedMessageId = InMemoryId.of(1);
-        SetMailboxesResponse.Builder nonEmptyBuilder = SetMailboxesResponse.builder()
-                .destroyed(buildersDestroyedMessageId);
-        InMemoryId destroyedMessageId = InMemoryId.of(2);
-        SetMailboxesResponse testee = SetMailboxesResponse.builder()
-                .destroyed(destroyedMessageId)
-                .build();
-        // When
-        testee.mergeInto(nonEmptyBuilder);
-        SetMailboxesResponse mergedResponse = nonEmptyBuilder.build();
-
-        // Then
-        assertThat(mergedResponse.getDestroyed()).containsExactly(buildersDestroyedMessageId, destroyedMessageId);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesRequestTest.java
deleted file mode 100644
index 2add0c5..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesRequestTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMessagesRequestTest {
-
-    @Test
-    public void builderShouldThrowWhenAccountIdIsNotNull() {
-        assertThatThrownBy(() -> SetMessagesRequest.builder().accountId(""))
-            .isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenIfInStateIsNotNull() {
-        assertThatThrownBy(() -> SetMessagesRequest.builder().ifInState(""))
-            .isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldWork() {
-        ImmutableList<MessageId> destroy = ImmutableList.of(TestMessageId.of(4));
-
-        SetMessagesRequest expected = new SetMessagesRequest(Optional.empty(), Optional.empty(), ImmutableList.of(), ImmutableList.of(), ImmutableMap.of(), destroy);
-
-        SetMessagesRequest setMessagesRequest = SetMessagesRequest.builder()
-            .accountId(null)
-            .ifInState(null)
-            .create(ImmutableMap.of())
-            .update(ImmutableMap.of())
-            .destroy(destroy)
-            .build();
-
-        assertThat(setMessagesRequest).isEqualToComparingFieldByField(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesResponseTest.java
deleted file mode 100644
index 8160976..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetMessagesResponseTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.Instant;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.jmap.api.model.Preview;
-import org.apache.james.jmap.model.BlobId;
-import org.apache.james.jmap.model.message.view.MessageFullView;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class SetMessagesResponseTest {
-
-    private static final Preview PREVIEW = Preview.from("preview");
-
-    @Test
-    public void builderShouldThrowWhenAccountIdIsGiven() {
-        assertThatThrownBy(() -> SetMessagesResponse.builder().accountId(""))
-            .isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenOldStateGiven() {
-        assertThatThrownBy(() -> SetMessagesResponse.builder().oldState(""))
-            .isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenNewStateIsGiven() {
-        assertThatThrownBy(() -> SetMessagesResponse.builder().newState(""))
-            .isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldWork() {
-        Instant currentDate = Instant.now();
-        ImmutableMap<CreationMessageId, MessageFullView> created = ImmutableMap.of(CreationMessageId.of("user|created|1"),
-            MessageFullView.builder()
-                .id(TestMessageId.of(1))
-                .blobId(BlobId.of("blobId"))
-                .threadId("threadId")
-                .mailboxId(InMemoryId.of(123))
-                .headers(ImmutableMap.of("key", "value"))
-                .subject("subject")
-                .size(123)
-                .date(currentDate)
-                .preview(PREVIEW)
-                .hasAttachment(false)
-                .build());
-        ImmutableList<MessageId> updated = ImmutableList.of(TestMessageId.of(2));
-        ImmutableList<MessageId> destroyed = ImmutableList.of(TestMessageId.of(3));
-        ImmutableMap<CreationMessageId, SetError> notCreated = ImmutableMap.of(CreationMessageId.of("dead-beef-defec8"), SetError.builder().type(SetError.Type.INVALID_PROPERTIES).build());
-        ImmutableMap<MessageId, SetError> notUpdated = ImmutableMap.of(TestMessageId.of(4), SetError.builder().type(SetError.Type.INVALID_ARGUMENTS).build());
-        ImmutableMap<MessageId, SetError> notDestroyed  = ImmutableMap.of(TestMessageId.of(5), SetError.builder().type(SetError.Type.ERROR).build());
-        ImmutableMap<CreationMessageId, SetError> mdnNotSent = ImmutableMap.of(CreationMessageId.of("dead-beef-defec9"), SetError.builder().type(SetError.Type.NOT_FOUND).build());
-        ImmutableMap<CreationMessageId, MessageId> mdnSent = ImmutableMap.of(CreationMessageId.of("dead-beef-defed0"), TestMessageId.of(12));
-        SetMessagesResponse expected = new SetMessagesResponse(null, null, null, created, mdnSent, updated, destroyed, notCreated, mdnNotSent, notUpdated, notDestroyed);
-
-        SetMessagesResponse setMessagesResponse = SetMessagesResponse.builder()
-            .created(created)
-            .updated(updated)
-            .destroyed(destroyed)
-            .notCreated(notCreated)
-            .notUpdated(notUpdated)
-            .notDestroyed(notDestroyed)
-            .mdnNotSent(mdnNotSent)
-            .mdnSent(mdnSent)
-            .build();
-
-        assertThat(setMessagesResponse).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void mergeIntoShouldCopyItemsWhenBuilderIsEmpty() {
-        // Given
-        SetMessagesResponse.Builder emptyBuilder = SetMessagesResponse.builder();
-        SetMessagesResponse testee = SetMessagesResponse.builder()
-                .created(buildMessage(CreationMessageId.of("user|inbox|1"), TestMessageId.of(1)))
-                .updated(ImmutableList.of(TestMessageId.of(2)))
-                .destroyed(ImmutableList.of(TestMessageId.of(3)))
-                .notCreated(ImmutableMap.of(CreationMessageId.of("dead-beef-defec8"), SetError.builder().type(SetError.Type.INVALID_PROPERTIES).build()))
-                .notUpdated(ImmutableMap.of(TestMessageId.of(5), SetError.builder().type(SetError.Type.ERROR).build()))
-                .notDestroyed(ImmutableMap.of(TestMessageId.of(6), SetError.builder().type(SetError.Type.NOT_FOUND).build()))
-                .build();
-
-        // When
-        testee.mergeInto(emptyBuilder);
-        // Then
-        assertThat(emptyBuilder.build()).isEqualToComparingFieldByField(testee);
-    }
-
-    private ImmutableMap<CreationMessageId, MessageFullView> buildMessage(CreationMessageId creationMessageId, MessageId messageId) {
-        return ImmutableMap.of(creationMessageId, MessageFullView.builder()
-                .id(messageId)
-                .blobId(BlobId.of("blobId"))
-                .threadId("threadId")
-                .fluentMailboxIds()
-                .headers(ImmutableMap.of())
-                .subject("subject")
-                .size(0)
-                .date(Instant.now())
-                .preview(PREVIEW)
-                .hasAttachment(false)
-                .build());
-    }
-
-    @Test
-    public void mergeIntoShouldMergeUpdatedLists() {
-        // Given
-        MessageId buildersUpdatedMessageId = TestMessageId.of(1);
-        SetMessagesResponse.Builder nonEmptyBuilder = SetMessagesResponse.builder()
-                .updated(ImmutableList.of(buildersUpdatedMessageId));
-        MessageId updatedMessageId = TestMessageId.of(2);
-        SetMessagesResponse testee = SetMessagesResponse.builder()
-                .updated(ImmutableList.of(updatedMessageId))
-                .build();
-        // When
-        testee.mergeInto(nonEmptyBuilder);
-        SetMessagesResponse mergedResponse = nonEmptyBuilder.build();
-
-        // Then
-        assertThat(mergedResponse.getUpdated()).containsExactly(buildersUpdatedMessageId, updatedMessageId);
-    }
-
-    @Test
-    public void mergeIntoShouldMergeCreatedLists() {
-        // Given
-        CreationMessageId buildersCreatedMessageId = CreationMessageId.of("user|inbox|1");
-        SetMessagesResponse.Builder nonEmptyBuilder = SetMessagesResponse.builder()
-                .created(buildMessage(buildersCreatedMessageId, TestMessageId.of(1)));
-        CreationMessageId createdMessageId = CreationMessageId.of("user|inbox|2");
-        SetMessagesResponse testee = SetMessagesResponse.builder()
-                .created(buildMessage(createdMessageId, TestMessageId.of(2)))
-                .build();
-        // When
-        testee.mergeInto(nonEmptyBuilder);
-        SetMessagesResponse mergedResponse = nonEmptyBuilder.build();
-
-        // Then
-        assertThat(mergedResponse.getCreated().keySet()).containsExactly(buildersCreatedMessageId, createdMessageId);
-    }
-
-    @Test
-    public void mergeIntoShouldMergeDestroyedLists() {
-        // Given
-        MessageId buildersDestroyedMessageId = TestMessageId.of(1);
-        SetMessagesResponse.Builder nonEmptyBuilder = SetMessagesResponse.builder()
-                .destroyed(ImmutableList.of(buildersDestroyedMessageId));
-        MessageId destroyedMessageId = TestMessageId.of(2);
-        SetMessagesResponse testee = SetMessagesResponse.builder()
-                .destroyed(ImmutableList.of(destroyedMessageId))
-                .build();
-        // When
-        testee.mergeInto(nonEmptyBuilder);
-        SetMessagesResponse mergedResponse = nonEmptyBuilder.build();
-
-        // Then
-        assertThat(mergedResponse.getDestroyed()).containsExactly(buildersDestroyedMessageId, destroyedMessageId);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationRequestTest.java
deleted file mode 100644
index 84f5289..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationRequestTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.AbstractMap;
-import java.util.Optional;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableMap;
-
-public class SetVacationRequestTest {
-
-    public static final String VACATION_ID = "singleton";
-
-    @Test
-    public void setVacationRequestShouldBeConstructedWithTheRightInformation() {
-        VacationResponse vacationResponse = VacationResponse.builder().id(VACATION_ID).textBody(Optional.of("any message")).build();
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .update(ImmutableMap.of(VACATION_ID, vacationResponse))
-            .build();
-
-        assertThat(setVacationRequest.getUpdate()).containsExactly(new AbstractMap.SimpleEntry<>(VACATION_ID, vacationResponse));
-    }
-
-    @Test(expected = NotImplementedException.class)
-    public void accountIdIsNotImplemented() {
-        VacationResponse vacationResponse = VacationResponse.builder().id(VACATION_ID).textBody(Optional.of("any message")).build();
-        SetVacationRequest.builder()
-            .accountId("any")
-            .update(ImmutableMap.of(VACATION_ID, vacationResponse))
-            .build();
-    }
-
-    @Test
-    public void accountIdNullShouldBeConsideredAsNoAccountId() {
-        VacationResponse vacationResponse = VacationResponse.builder().id(VACATION_ID).textBody(Optional.of("any message")).build();
-        SetVacationRequest setVacationRequest = SetVacationRequest.builder()
-            .accountId(null)
-            .update(ImmutableMap.of(VACATION_ID, vacationResponse))
-            .build();
-
-        assertThat(setVacationRequest.getUpdate()).containsEntry(VACATION_ID, vacationResponse);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationResponseTest.java
deleted file mode 100644
index bd7d067..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/SetVacationResponseTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.AbstractMap;
-
-import org.junit.Test;
-
-public class SetVacationResponseTest {
-
-    public static final String UPDATED_VACATION_ID = "updatedVacationId";
-    public static final String NOT_UPDATED_VACATION_ID = "notUpdatedVacationId";
-    public static final String ERROR_TYPE = "Test Error";
-    public static final String ERROR_DESCRIPTION = "Because an error is needed";
-
-    @Test
-    public void setVacationResponseShouldBeConstructedWithTheRightInformation() {
-        SetError setError = SetError.builder().type(SetError.Type.ERROR).description(ERROR_DESCRIPTION).build();
-        SetVacationResponse setVacationResponse = SetVacationResponse.builder()
-            .updatedId(UPDATED_VACATION_ID)
-            .notUpdated(NOT_UPDATED_VACATION_ID, setError)
-            .build();
-
-        assertThat(setVacationResponse.getUpdated().get()).containsExactly(UPDATED_VACATION_ID);
-        assertThat(setVacationResponse.getNotUpdated().get()).containsExactly(new AbstractMap.SimpleEntry<>(NOT_UPDATED_VACATION_ID, setError));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UpdateMessagePatchTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UpdateMessagePatchTest.java
deleted file mode 100644
index 0c278ac..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UpdateMessagePatchTest.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import jakarta.mail.Flags;
-
-import org.apache.james.jmap.model.Keyword;
-import org.apache.james.mailbox.FlagsBuilder;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.google.common.collect.ImmutableMap;
-
-public class UpdateMessagePatchTest {
-    private static final String FORWARDED = "forwarded";
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
-    @Test
-    public void unsetUpdatePatchShouldBeValid() {
-        UpdateMessagePatch emptyPatch = UpdateMessagePatch.builder().build();
-        assertThat(emptyPatch.isValid()).isTrue();
-    }
-
-    @Test
-    public void builderShouldSetUnreadFalseWhenBuiltWithIsUnreadFalse() {
-        UpdateMessagePatch.builder().isUnread(false).build();
-    }
-
-    @Test
-    public void applyToStateShouldNotResetSystemFlagsWhenUsingOldKeywords() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .isAnswered(true)
-            .build();
-
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .containsExactly(Flags.Flag.ANSWERED, Flags.Flag.SEEN);
-    }
-
-    @Test
-    public void applyToStateShouldNotModifySpecifiedOldKeywordsWhenAlreadySet() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .isAnswered(true)
-            .build();
-
-        Flags isSeen = new Flags(Flags.Flag.ANSWERED);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .containsExactly(Flags.Flag.ANSWERED);
-    }
-
-    @Test
-    public void applyToStateShouldResetSpecifiedOldKeywords() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .isAnswered(false)
-            .build();
-
-        Flags isSeen = new Flags(Flags.Flag.ANSWERED);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .containsExactly();
-    }
-
-    @Test
-    public void applyStateShouldReturnNewFlagsWhenKeywords() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Answered", true,
-                "$Flagged", true);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .containsExactly(Flags.Flag.ANSWERED, Flags.Flag.FLAGGED);
-    }
-
-    @Test
-    public void applyStateShouldReturnRemoveFlagsWhenKeywords() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(ImmutableMap.of())
-            .build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        assertThat(testee.applyToState(isSeen).getSystemFlags()).isEmpty();
-    }
-
-    @Test
-    public void applyStateShouldThrowWhenKeywordsContainDeletedFlag() {
-        expectedException.expect(IllegalArgumentException.class);
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Deleted", true);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-
-        Flags currentFlags = new Flags(Flags.Flag.SEEN);
-
-        testee.applyToState(currentFlags);
-    }
-
-    @Test
-    public void applyStateShouldThrowWhenKeywordsContainRecentFlag() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Recent", true);
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-
-        Flags currentFlags = new Flags(Flags.Flag.SEEN);
-
-        testee.applyToState(currentFlags);
-    }
-
-    @Test
-    public void applyStateShouldReturnFlagsWithoutUserFlagWhenKeywordsContainForwarded() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Answered", Keyword.FLAG_VALUE,
-                FORWARDED, Keyword.FLAG_VALUE);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .doesNotContain(Flags.Flag.USER);
-    }
-
-    @Test
-    public void applyStateShouldReturnFlagsWithUserFlagStringWhenKeywordsContainForwarded() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Answered", Keyword.FLAG_VALUE,
-                FORWARDED, Keyword.FLAG_VALUE);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        assertThat(testee.applyToState(isSeen).getUserFlags())
-            .containsExactly(FORWARDED);
-    }
-
-    @Test
-    public void applyStateShouldReturnFlagsWithDeletedFlagWhenKeywordsDoNotContainDeletedButOriginFlagsHaveDeleted() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Answered", Keyword.FLAG_VALUE);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-        Flags isSeen = new Flags(Flags.Flag.DELETED);
-        assertThat(testee.applyToState(isSeen).getSystemFlags())
-            .containsOnly(Flags.Flag.DELETED, Flags.Flag.ANSWERED);
-    }
-
-    @Test
-    public void applyStateShouldReturnFlagsWithRecentFlagWhenKeywordsDoNotContainRecentButOriginFlagsHaveRecent() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-                "$Answered", Keyword.FLAG_VALUE);
-
-        UpdateMessagePatch testee = UpdateMessagePatch.builder()
-            .keywords(keywords)
-            .build();
-        Flags flags = FlagsBuilder.builder()
-            .add(Flags.Flag.DELETED, Flags.Flag.RECENT)
-            .build();
-        assertThat(testee.applyToState(flags).getSystemFlags())
-            .containsOnly(Flags.Flag.DELETED, Flags.Flag.RECENT, Flags.Flag.ANSWERED);
-    }
-
-    @Test
-    public void isIdentityShouldReturnTrueWhenNoFlagsAndEmptyKeywords() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder().build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isTrue();
-    }
-
-    @Test
-    public void isIdentityShouldReturnFalseWhenFlagsAnsweredUpdated() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-            .isAnswered(true)
-            .build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isFalse();
-    }
-
-    @Test
-    public void isIdentityShouldReturnFalseWhenFlagsUnreadUpdated() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-            .isUnread(true)
-            .build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isFalse();
-    }
-
-    @Test
-    public void isIdentityShouldReturnFalseWhenFlagsFlaggedUpdated() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-            .isFlagged(true)
-            .build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isFalse();
-    }
-
-    @Test
-    public void isIdentityShouldReturnFalseWhenKeywords() {
-        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
-            "$Answered", Keyword.FLAG_VALUE,
-            "$Flagged", Keyword.FLAG_VALUE);
-
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-                .keywords(keywords)
-                .build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isFalse();
-    }
-
-    @Test
-    public void isIdentityShouldReturnFalseWhenEmptyKeywords() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-                .keywords(ImmutableMap.of())
-                .build();
-
-        assertThat(messagePatch.isFlagsIdentity()).isFalse();
-    }
-
-    @Test
-    public void isIdentityShouldThrowWhenBothIsFlagAndKeywords() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-                .keywords(ImmutableMap.of())
-                .isAnswered(false)
-                .build();
-
-        messagePatch.isFlagsIdentity();
-    }
-
-    @Test
-    public void applyStateShouldKeepKeywordsWhenNoKeywordPatchDefined() {
-        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
-            .build();
-        Flags flags = FlagsBuilder.builder()
-            .add(Flags.Flag.DELETED, Flags.Flag.RECENT, Flags.Flag.DRAFT)
-            .build();
-        assertThat(messagePatch.applyToState(flags)).isEqualTo(flags);
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UploadResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UploadResponseTest.java
deleted file mode 100644
index 904f363..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/UploadResponseTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- * http://www.apache.org/licenses/LICENSE-2.0                   *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ***************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import org.junit.jupiter.api.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-class UploadResponseTest {
-    @Test
-    void shouldRespectBeanContract() {
-        EqualsVerifier.forClass(UploadResponse.class).verify();
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/VacationResponseTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/VacationResponseTest.java
deleted file mode 100644
index e566ae2..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/VacationResponseTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-import org.apache.james.vacation.api.Vacation;
-import org.junit.Test;
-
-public class VacationResponseTest {
-
-    public static final String IDENTIFIER = "identifier";
-    public static final String MESSAGE = "A message explaining I am in vacation";
-    public static final String HTML_MESSAGE = "<p>A message explaining I am in vacation</p>";
-    public static final ZonedDateTime FROM_DATE = ZonedDateTime.parse("2016-04-15T11:56:32.224+07:00[Asia/Vientiane]");
-    public static final ZonedDateTime TO_DATE = ZonedDateTime.parse("2016-04-16T11:56:32.224+07:00[Asia/Vientiane]");
-    public static final String SUBJECT = "subject";
-
-    @Test
-    public void vacationResponseBuilderShouldBeConstructedWithTheRightInformation() {
-        VacationResponse vacationResponse = VacationResponse.builder()
-            .id(IDENTIFIER)
-            .enabled(true)
-            .fromDate(Optional.of(FROM_DATE))
-            .toDate(Optional.of(TO_DATE))
-            .textBody(Optional.of(MESSAGE))
-            .subject(Optional.of(SUBJECT))
-            .htmlBody(Optional.of(HTML_MESSAGE))
-            .build();
-
-        assertThat(vacationResponse.getId()).isEqualTo(IDENTIFIER);
-        assertThat(vacationResponse.isEnabled()).isEqualTo(true);
-        assertThat(vacationResponse.getTextBody()).contains(MESSAGE);
-        assertThat(vacationResponse.getHtmlBody()).contains(HTML_MESSAGE);
-        assertThat(vacationResponse.getFromDate()).contains(FROM_DATE);
-        assertThat(vacationResponse.getToDate()).contains(TO_DATE);
-        assertThat(vacationResponse.getSubject()).contains(SUBJECT);
-    }
-
-    @Test
-    public void vacationResponseShouldBeValidIfIdIsMissing() {
-        VacationResponse vacationResponse = VacationResponse.builder().build();
-
-        assertThat(vacationResponse.isValid()).isTrue();
-    }
-
-    @Test
-    public void vacationResponseShouldBeValidIfRightId() {
-        VacationResponse vacationResponse = VacationResponse.builder().id(Vacation.ID).build();
-
-        assertThat(vacationResponse.isValid()).isTrue();
-    }
-
-    @Test
-    public void vacationResponseShouldBeInvalidIfWrongId() {
-        VacationResponse vacationResponse = VacationResponse.builder().id(IDENTIFIER).build();
-
-        assertThat(vacationResponse.isValid()).isFalse();
-    }
-
-    @Test
-    public void vacationResponseShouldBeValidIfEnabledSetToFalse() {
-        VacationResponse vacationResponse = VacationResponse.builder().enabled(false).build();
-
-        assertThat(vacationResponse.isValid()).isTrue();
-    }
-
-    @Test
-    public void vacationResponseShouldBeValidIfEnabledSetToTrue() {
-        VacationResponse vacationResponse = VacationResponse.builder().enabled(true).build();
-
-        assertThat(vacationResponse.isValid()).isTrue();
-    }
-
-    @Test
-    public void subjectShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().subject(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void fromDateShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().fromDate(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void toDateShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().toDate(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void textBodyShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().textBody(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void htmlBodyShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().htmlBody(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void idStringShouldThrowNPEOnNullValue() throws Exception {
-        assertThatThrownBy(() -> VacationResponse.builder().id(null)).isInstanceOf(NullPointerException.class);
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequestTest.java
deleted file mode 100644
index c65c10e..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxCreateRequestTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model.mailbox;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.mailbox.Role;
-import org.junit.Test;
-
-public class MailboxCreateRequestTest {
-
-    @Test
-    public void builderShouldThrowOnNullRole() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().role(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void builderShouldThrowWhenRoleDefine() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().role(Role.ARCHIVE)).isInstanceOf(NotImplementedException.class);
-    }
-
-    @Test
-    public void builderShouldThrowOnNullId() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().id(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void builderShouldThrowOnNullSortOrder() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().sortOrder(null)).isInstanceOf(NullPointerException.class);
-    }
-    
-    @Test
-    public void builderShouldThrowOnSortOrderDefine() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().sortOrder(SortOrder.of(123))).isInstanceOf(NotImplementedException.class);
-    }    
-
-    @Test
-    public void builderShouldThrowOnNullParentId() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().parentId(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void builderShouldThrowOnNullName() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().name(null)).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void builderShouldRequireName() {
-        assertThatThrownBy(() -> MailboxCreateRequest.builder().build())
-            .isInstanceOf(IllegalStateException.class)
-            .hasMessageContaining("name");
-    }
-
-    @Test
-    public void builderShouldBuildWhenName() {
-        MailboxCreateRequest request = MailboxCreateRequest.builder().name("foo").build();
-        assertThat(request.getName()).isEqualTo("foo");
-        assertThat(request.getId()).isEmpty();
-        assertThat(request.getParentId()).isEmpty();
-        assertThat(request.getSortOrder()).isEmpty();
-        assertThat(request.getRole()).isEmpty();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespaceTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespaceTest.java
deleted file mode 100644
index 6af421d..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxNamespaceTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.mailbox;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.james.core.Username;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class MailboxNamespaceTest {
-    @Test
-    public void shouldRespectJavaBeanContract() throws Exception {
-        EqualsVerifier.forClass(MailboxNamespace.class)
-            .verify();
-    }
-
-    @Test
-    public void delegatedShouldThrowWhenNullOwner() throws Exception {
-        assertThatThrownBy(() -> MailboxNamespace.delegated(null))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void delegatedShouldThrowWhenEmptyOwner() throws Exception {
-        assertThatThrownBy(() -> MailboxNamespace.delegated(Username.of("")))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void delegatedShouldThrowWhenBlankOwner() throws Exception {
-        assertThatThrownBy(() -> MailboxNamespace.delegated(Username.of("  ")))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void delegatedShouldReturnDelegatedNamespace() throws Exception {
-        Username owner = Username.of("owner@test.com");
-        MailboxNamespace actualNamespace = MailboxNamespace.delegated(owner);
-
-        assertThat(actualNamespace.getType()).isEqualTo(MailboxNamespace.Type.Delegated);
-        assertThat(actualNamespace.getOwner().get()).isEqualTo(owner);
-    }
-
-    @Test
-    public void personalShouldReturnPersonalNamespace() throws Exception {
-        MailboxNamespace actualNamespace = MailboxNamespace.personal();
-
-        assertThat(actualNamespace.getOwner()).isEmpty();
-        assertThat(actualNamespace.getType()).isEqualTo(MailboxNamespace.Type.Personal);
-    }
-
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxTest.java
deleted file mode 100644
index 8f62d05..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.mailbox;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import org.apache.james.jmap.model.Number;
-import org.apache.james.jmap.model.mailbox.Rights;
-import org.apache.james.mailbox.Role;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.junit.Test;
-
-public class MailboxTest {
-
-    @Test(expected = NullPointerException.class)
-    public void idShouldThrowWhenIdIsNull() {
-        Mailbox.builder()
-            .id(null);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void nameShouldThrowWhenNameIsNull() {
-        Mailbox.builder()
-            .name(null);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void buildShouldThrowWhenIdIsNull() {
-        Mailbox.builder().build();
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void buildShouldThrowWhenNameIsNull() {
-        Mailbox.builder()
-            .id(InMemoryId.of(1))
-            .build();
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void buildShouldThrowWhenNameIsEmpty() {
-        Mailbox.builder()
-            .id(InMemoryId.of(1))
-            .name("")
-            .build();
-    }
-
-    @Test
-    public void buildShouldWork() {
-        Number totalMessages = Number.fromLong(456);
-        Number unreadMessages = Number.fromLong(789);
-        Number totalThreads = Number.fromLong(741);
-        Number unreadThreads = Number.fromLong(852);
-        Mailbox expectedMailbox = new Mailbox(InMemoryId.of(1), "name", Optional.of(InMemoryId.of(0)), Optional.of(Role.DRAFTS), SortOrder.of(123),
-            true, true, true, true, true, true, true,
-            totalMessages, unreadMessages, totalThreads, unreadThreads,
-            Rights.EMPTY, MailboxNamespace.personal(), Optional.empty());
-
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(1))
-            .name("name")
-            .parentId(InMemoryId.of(0))
-            .role(Optional.of(Role.DRAFTS))
-            .sortOrder(SortOrder.of(123))
-            .mustBeOnlyMailbox(true)
-            .mayReadItems(true)
-            .mayAddItems(true)
-            .mayRemoveItems(true)
-            .mayCreateChild(true)
-            .mayRename(true)
-            .mayDelete(true)
-            .totalMessages(456)
-            .unreadMessages(789)
-            .totalThreads(741)
-            .unreadThreads(852)
-            .build();
-
-        assertThat(mailbox).isEqualToComparingFieldByField(expectedMailbox);
-    }
-
-    @Test
-    public void parentIdDefaultValueIsEmpty() {
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(0))
-            .name("name")
-            .build();
-
-        assertThat(mailbox.getParentId()).isEmpty();
-    }
-
-    @Test
-    public void totalMessagesShouldNeverBeNegative() {
-        Mailbox mailbox = Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("name")
-                .totalMessages(-1234)
-                .build();
-
-        assertThat(mailbox.getTotalMessages()).isEqualTo(Number.ZERO);
-    }
-
-    @Test
-    public void unreadMessagesShouldNeverBeNegative() {
-        Mailbox mailbox = Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("name")
-                .unreadMessages(-1234)
-                .build();
-
-        assertThat(mailbox.getUnreadMessages()).isEqualTo(Number.ZERO);
-    }
-
-    @Test
-    public void totalMessagesShouldReturnZeroWhenZero() {
-        Mailbox mailbox = Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("name")
-                .totalMessages(0)
-                .build();
-
-        assertThat(mailbox.getTotalMessages()).isEqualTo(Number.ZERO);
-    }
-
-    @Test
-    public void unreadMessagesShouldReturnZeroWhenZero() {
-        Mailbox mailbox = Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("name")
-                .unreadMessages(0)
-                .build();
-
-        assertThat(mailbox.getUnreadMessages()).isEqualTo(Number.ZERO);
-    }
-
-    @Test
-    public void totalMessagesShouldAcceptPositiveValue() {
-        Mailbox mailbox = Mailbox.builder()
-                .id(InMemoryId.of(1))
-                .name("name")
-                .totalMessages(1234)
-                .build();
-
-        Number expectedTotalMessages = Number.fromLong(1234);
-        assertThat(mailbox.getTotalMessages()).isEqualTo(expectedTotalMessages);
-    }
-
-    @Test
-    public void unreadMessagesShouldAcceptPositiveValue() {
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(1))
-            .name("name")
-            .unreadMessages(1234)
-            .build();
-
-        Number expectedTotalMessages = Number.fromLong(1234);
-        assertThat(mailbox.getUnreadMessages()).isEqualTo(expectedTotalMessages);
-    }
-
-    @Test
-    public void hasRoleShouldReturnFalseWhenMailboxEmptyRole() {
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(0))
-            .name("name")
-            .build();
-
-        assertThat(mailbox.hasRole(Role.OUTBOX)).isFalse();
-    }
-
-    @Test
-    public void hasRoleShouldReturnFalseWhenMailboxDoesNotHaveSameRole() {
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(0))
-            .name("name")
-            .role(Optional.of(Role.DRAFTS))
-            .build();
-
-        assertThat(mailbox.hasRole(Role.OUTBOX)).isFalse();
-    }
-
-    @Test
-    public void hasRoleShouldReturnTrueWhenMailboxHasSameRole() {
-        Mailbox mailbox = Mailbox.builder()
-            .id(InMemoryId.of(0))
-            .name("name")
-            .role(Optional.of(Role.DRAFTS))
-            .build();
-
-        assertThat(mailbox.hasRole(Role.DRAFTS)).isTrue();
-    }
-
-    @Test
-    public void hasSystemRoleShouldReturnFalseWhenMailboxHasNotSameRole() throws Exception {
-        Mailbox mailbox = Mailbox.builder()
-            .name("mailbox")
-            .id(InMemoryId.of(0))
-            .build();
-
-        assertThat(mailbox.hasSystemRole()).isFalse();
-    }
-
-    @Test
-    public void hasSystemRoleShouldReturnFalseWhenMailboxHasNotSystemRole() throws Exception {
-        Mailbox mailbox = Mailbox.builder()
-            .name("mailbox")
-            .id(InMemoryId.of(0))
-            .role(Role.from("any"))
-            .build();
-
-        assertThat(mailbox.hasSystemRole()).isFalse();
-    }
-
-    @Test
-    public void hasSystemRoleShouldReturnTrueWhenMailboxHasSystemRole() throws Exception {
-        Mailbox mailbox = Mailbox.builder()
-            .name("mailbox")
-            .id(InMemoryId.of(0))
-            .role(Optional.of(Role.OUTBOX))
-            .build();
-
-        assertThat(mailbox.hasSystemRole()).isTrue();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequestTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequestTest.java
deleted file mode 100644
index 868ce5f..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/MailboxUpdateRequestTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.model.mailbox;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Optional;
-
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.junit.Test;
-
-public class MailboxUpdateRequestTest {
-    
-    @Test
-    public void getParentIdShouldReturnEmptyWhenNotGiven() throws Exception {
-        //Given
-        MailboxUpdateRequest testee = MailboxUpdateRequest.builder().name("my box").build();
-        //When
-        Optional<MailboxId> actual = testee.getParentId();
-        //Then
-        assertThat(actual).isEmpty();
-    }
-
-    @Test
-    public void getParentIdShouldReturnNullWhenNullParentIdGiven() throws Exception {
-        //Given
-        MailboxUpdateRequest testee = MailboxUpdateRequest.builder().name("my box").parentId(null).build();
-        //When
-        Optional<MailboxId> actual = testee.getParentId();
-        //Then
-        assertThat(actual).isNull();
-    }
-
-    @Test
-    public void getParentIdShouldReturnParentIdWhenParentIdGiven() throws Exception {
-        //Given
-        InMemoryId expected = InMemoryId.of(123);
-        MailboxUpdateRequest testee = MailboxUpdateRequest.builder().parentId(expected).name("my box").build();
-        //When
-        Optional<MailboxId> actual = testee.getParentId();
-        //Then
-        assertThat(actual).contains(expected);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/QuotaIdTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/QuotaIdTest.java
deleted file mode 100644
index fdb6b90..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/QuotaIdTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model.mailbox;
-
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class QuotaIdTest {
-
-    @Test
-    public void shouldRespectJavaBeanContract() {
-        EqualsVerifier.forClass(Quotas.QuotaId.class).verify();
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/SortOrderTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/SortOrderTest.java
deleted file mode 100644
index a65b26f..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/mailbox/SortOrderTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.draft.model.mailbox;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.util.TreeSet;
-
-import org.junit.Test;
-
-public class SortOrderTest {
-
-    @Test
-    public void sortOrderShouldNotBeNegative() {
-        assertThatThrownBy(() -> SortOrder.of(-1)).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void sortOrderShouldSupportZero() {
-        assertThat(SortOrder.of(0).getSortOrder()).isEqualTo(0);
-    }
-
-    @Test
-    public void sortOrderShouldSupportPositiveInteger() {
-        assertThat(SortOrder.of(4).getSortOrder()).isEqualTo(4);
-    }
-
-    @Test
-    public void sortOrderShouldBeComparable() {
-        TreeSet<SortOrder> sortedSet = new TreeSet<>();
-        SortOrder sixtySix = SortOrder.of(66);
-        SortOrder four = SortOrder.of(4);
-        SortOrder five = SortOrder.of(5);
-        sortedSet.add(sixtySix);
-        sortedSet.add(four);
-        sortedSet.add(five);
-        assertThat(sortedSet).containsExactly(four, five, sixtySix);
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java
deleted file mode 100644
index 6407852..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.send;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.jmap.send.MailMetadata;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.queue.api.MailQueue;
-import org.apache.james.queue.api.MailQueue.MailQueueItem;
-import org.apache.james.queue.api.MailQueueFactory;
-import org.apache.james.queue.api.RawMailQueueItemDecoratorFactory;
-import org.apache.james.queue.memory.MemoryMailQueueFactory;
-import org.apache.james.util.MimeMessageUtil;
-import org.apache.mailet.Attribute;
-import org.apache.mailet.AttributeValue;
-import org.apache.mailet.base.test.FakeMail;
-import org.junit.Before;
-import org.junit.Test;
-
-import reactor.core.publisher.Flux;
-
-public class MailSpoolTest {
-    private static final String USERNAME = "user";
-    private static final TestMessageId MESSAGE_ID = TestMessageId.of(1);
-    private static final String NAME = "Name";
-
-    private MailSpool mailSpool;
-    private MailQueue myQueue;
-
-    @Before
-    public void setup() {
-        MemoryMailQueueFactory mailQueueFactory = new MemoryMailQueueFactory(new RawMailQueueItemDecoratorFactory());
-        myQueue = mailQueueFactory.createQueue(MailQueueFactory.SPOOL);
-
-        mailSpool = new MailSpool(mailQueueFactory);
-        mailSpool.start();
-    }
-
-    @Test
-    public void sendShouldEnQueueTheMail() throws Exception {
-        FakeMail mail = FakeMail.builder()
-            .name(NAME)
-            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
-            .build();
-
-        mailSpool.send(mail, new MailMetadata(MESSAGE_ID, USERNAME)).block();
-
-        MailQueueItem actual = Flux.from(myQueue.deQueue()).blockFirst();
-        assertThat(actual.getMail().getName()).isEqualTo(NAME);
-    }
-
-    @Test
-    public void sendShouldPositionJMAPRelatedMetadata() throws Exception {
-        FakeMail mail = FakeMail.builder()
-            .name(NAME)
-            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
-            .build();
-
-        mailSpool.send(mail, new MailMetadata(MESSAGE_ID, USERNAME)).block();
-
-        MailQueueItem actual = Flux.from(myQueue.deQueue()).blockFirst();
-        assertThat(actual.getMail().getAttribute(MailMetadata.MAIL_METADATA_USERNAME_ATTRIBUTE))
-            .contains(new Attribute(MailMetadata.MAIL_METADATA_USERNAME_ATTRIBUTE, AttributeValue.of(USERNAME)));
-        assertThat(actual.getMail().getAttribute(MailMetadata.MAIL_METADATA_MESSAGE_ID_ATTRIBUTE))
-            .contains(new Attribute(MailMetadata.MAIL_METADATA_MESSAGE_ID_ATTRIBUTE, AttributeValue.of(MESSAGE_ID.serialize())));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DependencyGraphTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DependencyGraphTest.java
deleted file mode 100644
index 7188e96..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DependencyGraphTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.james.jmap.draft.utils.DependencyGraph.CycleDetectedException;
-import org.junit.Test;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class DependencyGraphTest {
-
-    @Test
-    public void getBuildChainShouldReturnOrderedMailbox() {
-        // Given
-        Commit a = new Commit("A");
-        Commit b = new Commit("B", a);
-        Commit c = new Commit("C", b);
-
-        DependencyGraph<Commit> graph = new DependencyGraph<>(Commit::getParent);
-        Stream.of(b, a, c)
-            .forEach(graph::registerItem);
-
-        // When
-        Stream<Commit> orderedMailboxes = graph.getBuildChain();
-
-        // Then
-        assertThat(orderedMailboxes).extracting(Commit::getMessage).containsExactly("A", "B", "C");
-    }
-
-    @Test
-    public void getBuildChainWithEmptyGraphShouldReturnEmpty() {
-        DependencyGraph<Commit> graph = new DependencyGraph<>(m -> null);
-        assertThat(graph.getBuildChain()).isEmpty();
-    }
-
-    @Test
-    public void getBuildChainOnIsolatedVerticesShouldReturnSameOrder() {
-        DependencyGraph<Commit> graph = new DependencyGraph<>(m -> Optional.empty());
-        ImmutableList<Commit> isolatedMailboxes = ImmutableList.of(new Commit("A"), new Commit("B"), new Commit("C"));
-        isolatedMailboxes.forEach(graph::registerItem);
-
-        List<Commit> orderedResultList = graph.getBuildChain().collect(Collectors.toList());
-
-        assertThat(orderedResultList).isEqualTo(isolatedMailboxes);
-    }
-
-    @Test
-    public void getBuildChainOnTwoIsolatedTreesShouldWork() {
-        // a-b  d-e
-        //  \c   \f
-
-        //Given
-        Commit a = new Commit("A");
-        Commit b = new Commit("B", a);
-        Commit c = new Commit("C", b);
-        Commit d = new Commit("D");
-        Commit e = new Commit("E", d);
-        Commit f = new Commit("F", d);
-        DependencyGraph<Commit> testee = new DependencyGraph<>(Commit::getParent);
-        Stream.of(b, a, e, d, f, c)
-            .forEach(testee::registerItem);
-
-        //When
-        Stream<Commit> actual = testee.getBuildChain();
-
-        //Then
-        assertThat(actual).extracting(Commit::getMessage).containsExactly("A", "D", "B", "E", "F", "C");
-    }
-
-    @Test
-    public void getBuildChainOnComplexTreeShouldWork() {
-        //Given
-        Commit a = new Commit("A");
-        Commit b = new Commit("B", a);
-        Commit c = new Commit("C", a);
-        Commit d = new Commit("D", b);
-        Commit e = new Commit("E", b);
-        Commit f = new Commit("F", c);
-        Commit g = new Commit("G", e);
-        DependencyGraph<Commit> testee = new DependencyGraph<>(Commit::getParent);
-        Stream.of(b, a, e, g, d, f, c)
-            .forEach(testee::registerItem);
-
-        //When
-        Stream<Commit> actual = testee.getBuildChain();
-
-        //Then
-        assertThat(actual).extracting(Commit::getMessage).containsExactly("A", "B", "C", "E", "D", "F", "G");
-    }
-    
-    @Test(expected = CycleDetectedException.class)
-    public void getBuildChainOnTreeWithLoopShouldFail() {
-        //Given
-        Commit a = new Commit("A");
-        Commit b = new Commit("B", a);
-        a.setParent(b);
-        DependencyGraph<Commit> testee = new DependencyGraph<>(Commit::getParent);
-        Stream.of(a, b)
-            .forEach(testee::registerItem);
-
-        //When
-        testee.getBuildChain();
-    }
-    
-    @Test(expected = CycleDetectedException.class)
-    public void getBuildChainOnTreeWithComplexLoopShouldFail() {
-        // a - b
-        // c - d - e - f
-        // |___________|
-        //Given
-        Commit a = new Commit("A");
-        Commit b = new Commit("B", a);
-        
-        Commit c = new Commit("C");
-        Commit d = new Commit("D", c);
-        Commit e = new Commit("E", d);
-        Commit f = new Commit("F", e);
-        c.setParent(f);
-        DependencyGraph<Commit> testee = new DependencyGraph<>(Commit::getParent);
-        Stream.of(a, b, c, d, e, f)
-            .forEach(testee::registerItem);
-
-        //When
-        testee.getBuildChain();
-    }
-
-
-    private static class Commit {
-        private final String message;
-        private Optional<Commit> parent;
-
-        @VisibleForTesting
-        Commit(String message) {
-            this(message, null);
-        }
-
-        @VisibleForTesting
-        Commit(String message, Commit parent) {
-            Preconditions.checkArgument(message != null);
-            this.message = message;
-            this.parent = Optional.ofNullable(parent);
-        }
-
-        public Optional<Commit> getParent() {
-            return parent;
-        }
-        
-        public void setParent(Commit parent) {
-            this.parent = Optional.of(parent);
-        }
-
-        public String getMessage() {
-            return message;
-        }
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java
deleted file mode 100644
index f752425..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java
+++ /dev/null
@@ -1,457 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.ZonedDateTime;
-import java.util.Date;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import jakarta.mail.Flags.Flag;
-
-import org.apache.james.jmap.draft.model.Filter;
-import org.apache.james.jmap.draft.model.FilterCondition;
-import org.apache.james.jmap.draft.model.FilterOperator;
-import org.apache.james.jmap.draft.model.Header;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.AddressType;
-import org.apache.james.mailbox.model.SearchQuery.DateResolution;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class FilterToCriteriaTest {
-    private static final String FORWARDED = "forwarded";
-
-    @Test
-    public void filterConditionShouldThrowWhenUnknownFilter() {
-        Filter myFilter = (indentation -> null);
-        assertThatThrownBy(() -> new FilterToCriteria().convert(myFilter))
-            .isInstanceOf(RuntimeException.class)
-            .hasMessage("Unknown filter: " + myFilter.getClass());
-    }
-
-    @Test
-    public void filterConditionShouldMapEmptyWhenEmptyFilter() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder().build());
-
-        assertThat(criteria).isEmpty();
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenFrom() {
-        String from = "sender@james.org";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .from(from)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.address(AddressType.From, from));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenTo() {
-        String to = "recipient@james.org";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .to(to)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.address(AddressType.To, to));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenCc() {
-        String cc = "copy@james.org";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .cc(cc)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.address(AddressType.Cc, cc));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHasAttachment() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .hasAttachment(true)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.hasAttachment());
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHasNoAttachment() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .hasAttachment(false)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.hasNoAttachment());
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenBcc() {
-        String bcc = "blindcopy@james.org";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .bcc(bcc)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.address(AddressType.Bcc, bcc));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenSubject() {
-        String subject = "subject";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .subject(subject)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.subject(subject));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenBody() {
-        String body = "body";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .body(body)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.bodyContains(body));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenAttachments() {
-        String attachments = "attachments";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .attachments(attachments)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.attachmentContains(attachments));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenText() {
-        String text = "text";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .text(text)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.or(ImmutableList.of(
-            SearchQuery.address(AddressType.From, text),
-            SearchQuery.address(AddressType.To, text),
-            SearchQuery.address(AddressType.Cc, text),
-            SearchQuery.address(AddressType.Bcc, text),
-            SearchQuery.subject(text),
-            SearchQuery.bodyContains(text),
-            SearchQuery.attachmentContains(text),
-            SearchQuery.attachmentFileName(text))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenAfter() {
-        ZonedDateTime after = ZonedDateTime.now();
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .after(after)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.sentDateAfter(Date.from(after.toInstant()), DateResolution.Second));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenBefore() {
-        ZonedDateTime before = ZonedDateTime.now();
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .before(before)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.sentDateBefore(Date.from(before.toInstant()), DateResolution.Second));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsAnswered() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .isAnswered(true)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(Flag.ANSWERED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsDraft() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .isDraft(true)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(Flag.DRAFT));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsFlagged() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .isFlagged(true)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(Flag.FLAGGED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsUnread() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .isUnread(true)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(Flag.SEEN));
-    }
-
-
-    @Test
-    public void filterConditionShouldMapWhenIsNotAnswered() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .isAnswered(false)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(Flag.ANSWERED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsNotDraft() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .isDraft(false)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(Flag.DRAFT));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsNotFlagged() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .isFlagged(false)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(Flag.FLAGGED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenIsRead() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .isUnread(false)
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(Flag.SEEN));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenMaxSize() {
-        int maxSize = 123;
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .maxSize(maxSize)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.sizeLessThan(maxSize));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenMinSize() {
-        int minSize = 4;
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .minSize(minSize)
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.sizeGreaterThan(minSize));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHeaderWithOneElement() {
-        String headerName = "name";
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .header(Header.from(ImmutableList.of(headerName)))
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.headerExists(headerName));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHeaderWithTwoElements() {
-        String headerName = "name";
-        String headerValue = "value";
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .header(Header.from(ImmutableList.of(headerName, headerValue)))
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.headerContains(headerName, headerValue));
-    }
-
-    @Test
-    public void filterConditionShouldMapTwoConditions() {
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        Filter filter = FilterOperator.and(
-                FilterCondition.builder()
-                    .from(from)
-                    .build(),
-                FilterCondition.builder()
-                    .to(to)
-                    .build());
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(filter);
-
-        assertThat(criteria).containsExactly(SearchQuery.and(ImmutableList.of(
-            SearchQuery.address(AddressType.From, from),
-            SearchQuery.address(AddressType.To, to))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenAndOperator() {
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        String subject = "subject";
-
-        Filter complexFilter = FilterOperator.and(
-                FilterCondition.builder()
-                    .from(from)
-                    .to(to)
-                    .subject(subject)
-                    .build());
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(complexFilter);
-
-        assertThat(criteria).containsExactly(SearchQuery.and(ImmutableList.of(
-            SearchQuery.address(AddressType.From, from),
-            SearchQuery.address(AddressType.To, to),
-            SearchQuery.subject(subject))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenOrOperator() {
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        String subject = "subject";
-
-        Filter complexFilter = FilterOperator.or(
-                FilterCondition.builder()
-                    .from(from)
-                    .to(to)
-                    .subject(subject)
-                    .build());
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(complexFilter);
-
-        assertThat(criteria).containsExactly(SearchQuery.or(ImmutableList.of(
-            SearchQuery.address(AddressType.From, from),
-            SearchQuery.address(AddressType.To, to),
-            SearchQuery.subject(subject))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenNotOperator() {
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        String subject = "subject";
-
-        Filter complexFilter = FilterOperator.not(
-                FilterCondition.builder()
-                    .from(from)
-                    .to(to)
-                    .subject(subject)
-                    .build());
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(complexFilter);
-
-        assertThat(criteria).containsExactly(SearchQuery.not(ImmutableList.of(
-            SearchQuery.address(AddressType.From, from),
-            SearchQuery.address(AddressType.To, to),
-            SearchQuery.subject(subject))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenComplexFilterTree() {
-        String from = "sender@james.org";
-        String to = "recipient@james.org";
-        String cc = "copy@james.org";
-
-        Filter complexFilter = FilterOperator.and(
-                FilterCondition.builder()
-                    .from(from)
-                    .build(),
-                FilterOperator.or(
-                    FilterOperator.not(
-                        FilterCondition.builder()
-                            .to(to)
-                            .build()),
-                    FilterCondition.builder()
-                        .cc(cc)
-                        .build()));
-
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(complexFilter);
-
-        assertThat(criteria).containsExactly(SearchQuery.and(ImmutableList.of(
-            SearchQuery.address(AddressType.From, from),
-            SearchQuery.or(ImmutableList.of(
-                SearchQuery.not(SearchQuery.address(AddressType.To, to)),
-                SearchQuery.address(AddressType.Cc, cc))))));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHasKeyword() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .hasKeyword(Optional.of("$Flagged"))
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(Flag.FLAGGED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenHasKeywordWithUserFlag() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .hasKeyword(Optional.of(FORWARDED))
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsSet(FORWARDED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenNotKeyword() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-            .notKeyword(Optional.of("$Flagged"))
-            .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(Flag.FLAGGED));
-    }
-
-    @Test
-    public void filterConditionShouldMapWhenNotKeywordWithUserFlag() {
-        Stream<SearchQuery.Criterion> criteria = new FilterToCriteria().convert(FilterCondition.builder()
-                .notKeyword(Optional.of(FORWARDED))
-                .build());
-
-        assertThat(criteria).containsExactly(SearchQuery.flagIsUnSet(FORWARDED));
-    }
-
-    @Test
-    public void attachmentFileNameShouldMapWhenHasAttachmentFileName() {
-        String fileName = "file.gz";
-
-        assertThat(new FilterToCriteria().convert(FilterCondition.builder()
-            .attachmentFileName(Optional.of(fileName))
-            .build()))
-            .containsExactly(SearchQuery.attachmentFileName(fileName));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/MailboxUtilsTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/MailboxUtilsTest.java
deleted file mode 100644
index b740d89..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/MailboxUtilsTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.james.core.Username;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.MailboxConstants;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.junit.Before;
-import org.junit.Test;
-
-public class MailboxUtilsTest {
-
-    private MailboxManager mailboxManager;
-    private MailboxSession mailboxSession;
-    private Username user;
-    private MailboxUtils sut;
-
-    @Before
-    public void setup() throws Exception {
-        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
-        user = Username.of("user@domain.org");
-        mailboxSession = mailboxManager.createSystemSession(user);
-        sut = new MailboxUtils(mailboxManager);
-    }
-    
-    @Test
-    public void hasChildrenShouldReturnFalseWhenNoChild() throws Exception {
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "myBox");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-        MailboxId mailboxId = mailboxManager.getMailbox(mailboxPath, mailboxSession).getId();
-
-        assertThat(sut.hasChildren(mailboxId, mailboxSession)).isFalse();
-    }
-
-    @Test
-    public void hasChildrenShouldReturnTrueWhenHasAChild() throws Exception {
-        MailboxPath parentMailboxPath = MailboxPath.forUser(user, MailboxConstants.INBOX);
-        mailboxManager.createMailbox(parentMailboxPath, mailboxSession);
-        MailboxId parentId = mailboxManager.getMailbox(parentMailboxPath, mailboxSession).getId();
-
-        MailboxPath mailboxPath = MailboxPath.forUser(user, "INBOX.myBox");
-        mailboxManager.createMailbox(mailboxPath, mailboxSession);
-
-        assertThat(sut.hasChildren(parentId, mailboxSession)).isTrue();
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortConverterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortConverterTest.java
deleted file mode 100644
index ae66271..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortConverterTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.List;
-
-import org.apache.james.mailbox.model.SearchQuery.Sort;
-import org.apache.james.mailbox.model.SearchQuery.Sort.Order;
-import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-public class SortConverterTest {
-
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
-    @Test
-    public void convertToSortsShouldThrowOnNullValue() {
-        expectedException.expect(NullPointerException.class);
-        SortConverter.convertToSorts(null);
-    }
-
-    @Test
-    public void convertToSortsShouldReturnEmptyOnEmptyEntry() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of()))
-            .isEmpty();
-    }
-
-    @Test
-    public void convertToSortsShouldThrowOnNullJmapSort() {
-        List<String> jmapSorts = Lists.newArrayList((String) null);
-        expectedException.expect(NullPointerException.class);
-        SortConverter.convertToSorts(jmapSorts);
-    }
-
-    @Test
-    public void convertToSortsShouldThrowOnEmptyJmapSort() {
-        expectedException.expect(IllegalArgumentException.class);
-        SortConverter.convertToSorts(ImmutableList.of(""));
-    }
-
-    @Test
-    public void convertToSortsShouldThrowOnUnknownJmapSort() {
-        expectedException.expect(IllegalArgumentException.class);
-        SortConverter.convertToSorts(ImmutableList.of("unknown"));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportDate() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("date desc")))
-            .containsExactly(new Sort(SortClause.SentDate, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportId() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("id desc")))
-            .containsExactly(new Sort(SortClause.Id, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldBeDescWhenNoOrderClause() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("date")))
-            .containsExactly(new Sort(SortClause.SentDate, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldThrowWhenOnUnknownOrderClause() {
-        expectedException.expect(IllegalArgumentException.class);
-        SortConverter.convertToSorts(ImmutableList.of("date unknown"));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportAscOrder() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("date asc")))
-            .containsExactly(new Sort(SortClause.SentDate, Order.NATURAL));
-    }
-
-    @Test
-    public void convertToSortsShouldThrowWhenJmapSortIsTooLong() {
-        expectedException.expect(IllegalArgumentException.class);
-        SortConverter.convertToSorts(ImmutableList.of("date asc toomuch"));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportMultipleSorts() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("date asc", "id desc")))
-            .containsExactly(new Sort(SortClause.SentDate, Order.NATURAL),
-                new Sort(SortClause.Id, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportSubject() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("subject desc")))
-                .containsExactly(new Sort(SortClause.BaseSubject, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportFrom() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("from desc")))
-                .containsExactly(new Sort(SortClause.MailboxFrom, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportTo() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("to desc")))
-                .containsExactly(new Sort(SortClause.MailboxTo, Order.REVERSE));
-    }
-
-    @Test
-    public void convertToSortsShouldSupportSize() {
-        assertThat(SortConverter.convertToSorts(ImmutableList.of("size desc")))
-                .containsExactly(new Sort(SortClause.Size, Order.REVERSE));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollectionsTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollectionsTest.java
deleted file mode 100644
index 77c6293..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/SortingHierarchicalCollectionsTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.draft.utils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.utils.DependencyGraph.CycleDetectedException;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.model.MailboxId;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class SortingHierarchicalCollectionsTest {
-    private static final InMemoryId INBOX_ID = InMemoryId.of(0);
-    private static final InMemoryId A_ID = InMemoryId.of(1);
-    private static final InMemoryId B_ID = InMemoryId.of(2);
-    private static final InMemoryId C_ID = InMemoryId.of(3);
-    private static final InMemoryId D_ID = InMemoryId.of(4);
-    private static final InMemoryId E_ID = InMemoryId.of(5);
-
-    private SortingHierarchicalCollections<Mailbox, MailboxId> sut;
-
-    @Before
-    public void setup() {
-        sut = new SortingHierarchicalCollections<>(Mailbox::getId, Mailbox::getParentId);
-    }
-
-    @Test
-    public void sortFromRootToLeafShouldReturnOrderedMailbox() {
-        // Given
-        Mailbox inbox = Mailbox.builder().name("INBOX").id(INBOX_ID).build();
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).parentId(INBOX_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).parentId(INBOX_ID).build();
-        Mailbox c = Mailbox.builder().name("C").id(C_ID).parentId(B_ID).build();
-        Mailbox d = Mailbox.builder().name("D").id(D_ID).parentId(A_ID).build();
-        Mailbox e = Mailbox.builder().name("E").id(E_ID).parentId(C_ID).build();
-        ImmutableList<Mailbox> input = ImmutableList.of(b, c, d, a, inbox, e);
-
-        // When
-        List<Mailbox> result = sut.sortFromRootToLeaf(input);
-
-        // Then
-        assertThat(result).extracting(Mailbox::getName).endsWith("C", "D", "E").startsWith("INBOX");
-    }
-
-    @Test
-    public void sortFromRootToLeafEmptyMailboxShouldReturnEmpty() {
-        ImmutableList<Mailbox> input = ImmutableList.of();
-        List<Mailbox> result = sut.sortFromRootToLeaf(input);
-        assertThat(result).isEmpty();
-    }
-
-    @Test
-    public void sortFromRootToLeafOrphanMailboxesShouldReturnInput() {
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).build();
-        Mailbox c = Mailbox.builder().name("C").id(C_ID).build();
-
-        ImmutableList<Mailbox> input = ImmutableList.of(a, b, c);
-        List<String> result = sut.sortFromRootToLeaf(input).stream()
-                .map(Mailbox::getName)
-                .collect(Collectors.toList());
-
-        assertThat(result).containsExactly("A", "B", "C");
-    }
-
-    @Test(expected = CycleDetectedException.class)
-    public void sortFromRootToLeafWithLoopShouldThrow() {
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).parentId(B_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).parentId(A_ID).build();
-
-        ImmutableList<Mailbox> input = ImmutableList.of(a, b);
-
-        sut.sortFromRootToLeaf(input);
-    }
-
-    @Test
-    public void sortFromLeafToRootShouldReturnOrderedMailbox() {
-        //Given
-        Mailbox inbox = Mailbox.builder().name("INBOX").id(INBOX_ID).build();
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).parentId(INBOX_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).parentId(INBOX_ID).build();
-        Mailbox c = Mailbox.builder().name("C").id(C_ID).parentId(B_ID).build();
-        Mailbox d = Mailbox.builder().name("D").id(D_ID).parentId(A_ID).build();
-        Mailbox e = Mailbox.builder().name("E").id(E_ID).parentId(C_ID).build();
-
-        ImmutableList<Mailbox> input = ImmutableList.of(b, c, d, a, inbox, e);
-
-        //When
-        List<Mailbox> result = sut.sortFromLeafToRoot(input);
-
-        assertThat(result).extracting(Mailbox::getName).endsWith("INBOX").startsWith("E");
-    }
-
-    @Test
-    public void sortFromLeafToRootEmptyMailboxShouldReturnEmpty() {
-        ImmutableList<Mailbox> input = ImmutableList.of();
-        List<Mailbox> result = sut.sortFromLeafToRoot(input);
-        assertThat(result).isEmpty();
-    }
-
-    @Test
-    public void sortFromLeafToRootOrphanMailboxesShouldReturnInput() {
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).build();
-        Mailbox c = Mailbox.builder().name("C").id(C_ID).build();
-
-        ImmutableList<Mailbox> input = ImmutableList.of(a, b, c);
-        List<String> result = sut.sortFromLeafToRoot(input).stream()
-                .map(Mailbox::getName)
-                .collect(Collectors.toList());
-
-        assertThat(result).containsExactly("C", "B", "A");
-    }
-
-    @Test(expected = CycleDetectedException.class)
-    public void sortFromLeafToRootWithLoopShouldThrow() {
-        Mailbox a = Mailbox.builder().name("A").id(A_ID).parentId(B_ID).build();
-        Mailbox b = Mailbox.builder().name("B").id(B_ID).parentId(A_ID).build();
-
-        ImmutableList<Mailbox> input = ImmutableList.of(a, b);
-
-        sut.sortFromLeafToRoot(input);
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategyTest.java
deleted file mode 100644
index 34062a5..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AccessTokenAuthenticationStrategyTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.UUID;
-import java.util.function.Predicate;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
-import org.apache.james.jmap.draft.crypto.AccessTokenManagerImpl;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.SessionProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import io.netty.handler.codec.http.HttpHeaders;
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class AccessTokenAuthenticationStrategyTest {
-    private static final String AUTHORIZATION_HEADERS = "Authorization";
-
-    private AccessTokenManagerImpl mockedAccessTokenManager;
-    private MailboxManager mockedMailboxManager;
-    private AccessTokenAuthenticationStrategy testee;
-    private HttpServerRequest mockedRequest;
-    private HttpHeaders mockedHeaders;
-
-    @Before
-    public void setup() {
-        mockedAccessTokenManager = mock(AccessTokenManagerImpl.class);
-        mockedMailboxManager = mock(MailboxManager.class);
-        mockedRequest = mock(HttpServerRequest.class);
-        mockedHeaders = mock(HttpHeaders.class);
-
-        when(mockedRequest.requestHeaders())
-            .thenReturn(mockedHeaders);
-
-        testee = new AccessTokenAuthenticationStrategy(mockedAccessTokenManager, mockedMailboxManager);
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnEmptyWhenNoAuthProvided() {
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenAuthHeaderIsNotAnUUID() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn("bad");
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).block())
-                .isExactlyInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenAuthHeaderIsInvalid() {
-        Username username = Username.of("123456789");
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-
-        when(mockedMailboxManager.createSystemSession(eq(username)))
-            .thenReturn(fakeMailboxSession);
-
-        UUID authHeader = UUID.randomUUID();
-        AccessToken accessToken = AccessToken.fromString(authHeader.toString());
-        when(mockedAccessTokenManager.getUsernameFromToken(accessToken))
-            .thenReturn(Mono.error(new InvalidAccessToken(accessToken)));
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(authHeader.toString());
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).blockOptional())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenMultipleAuthHeaders() {
-        UUID authHeader1 = UUID.randomUUID();
-        UUID authHeader2 = UUID.randomUUID();
-
-        when(mockedHeaders.getAll(AUTHORIZATION_HEADERS))
-            .thenReturn(ImmutableList.of(authHeader1.toString(), authHeader2.toString()));
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).block())
-            .isExactlyInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnWhenAuthHeadersAreValid() {
-        Username username = Username.of("123456789");
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-
-        when(mockedMailboxManager.authenticate(eq(username)))
-            .thenReturn(new SessionProvider.AuthorizationStep() {
-                @Override
-                public MailboxSession as(Username other) {
-                    throw new NotImplementedException();
-                }
-
-                @Override
-                public MailboxSession withoutDelegation() {
-                    return fakeMailboxSession;
-                }
-
-                @Override
-                public MailboxSession forMatchingUser(Predicate<Username> other) throws MailboxException {
-                    throw new NotImplementedException();
-                }
-            });
-
-        UUID authHeader = UUID.randomUUID();
-        AccessToken accessToken = AccessToken.fromString(authHeader.toString());
-        when(mockedAccessTokenManager.getUsernameFromToken(accessToken))
-            .thenReturn(Mono.just(username));
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(authHeader.toString());
-
-
-        MailboxSession result = testee.createMailboxSession(mockedRequest).block();
-        assertThat(result).isEqualTo(fakeMailboxSession);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java
deleted file mode 100644
index b87f7b3..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Function;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.AccessTokenRepository;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import io.netty.handler.codec.http.HttpHeaders;
-import io.netty.handler.codec.http.HttpMethod;
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class AuthenticatorTest {
-    private static final String TOKEN = "df991d2a-1c5a-4910-a90f-808b6eda133e";
-    private static final String AUTHORIZATION_HEADERS = "Authorization";
-    private static final Username USERNAME = Username.of("user@domain.tld");
-
-    public static AuthenticationStrategy asAuthStrategy(Function<HttpServerRequest, Mono<MailboxSession>> auth) {
-        return new AuthenticationStrategy() {
-            @Override
-            public Mono<MailboxSession> createMailboxSession(HttpServerRequest httpRequest) {
-                return auth.apply(httpRequest);
-            }
-
-            @Override
-            public AuthenticationChallenge correspondingChallenge() {
-                return AuthenticationChallenge.of(AuthenticationScheme.of("Testing"), ImmutableMap.of());
-            }
-        };
-    }
-
-    private static final AuthenticationStrategy DENY = asAuthStrategy(httpRequest -> Mono.error(new UnauthorizedException(null)));
-    private static final AuthenticationStrategy ALLOW = asAuthStrategy(httpRequest -> Mono.just(mock(MailboxSession.class)));
-
-    private HttpServerRequest mockedRequest;
-    private HttpHeaders mockedHeaders;
-    private AccessTokenRepository accessTokenRepository;
-    private Authenticator testee;
-
-    @Before
-    public void setup() throws Exception {
-        mockedRequest = mock(HttpServerRequest.class);
-        mockedHeaders = mock(HttpHeaders.class);
-
-        accessTokenRepository = new MemoryAccessTokenRepository(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS));
-
-        when(mockedRequest.method())
-            .thenReturn(HttpMethod.POST);
-
-        when(mockedRequest.requestHeaders())
-            .thenReturn(mockedHeaders);
-
-        testee = Authenticator.of(new RecordingMetricFactory(), DENY);
-    }
-
-    @Test
-    public void filterShouldReturnUnauthorizedOnNullAuthorizationHeader() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(null);
-
-        assertThatThrownBy(() -> testee.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void filterShouldReturnUnauthorizedWhenNoAuthenticationStrategy() {
-        Authenticator testee = Authenticator.of(new RecordingMetricFactory());
-
-        assertThatThrownBy(() -> testee.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void authenticationStrategiesShouldNotBeEagerlySubScribed() {
-        AtomicBoolean called = new AtomicBoolean(false);
-
-        testee = Authenticator.of(new RecordingMetricFactory(),
-            ALLOW,
-            asAuthStrategy(req -> Mono.fromRunnable(() -> called.set(true))));
-        assertThat(called.get()).isFalse();
-
-        testee.authenticate(mockedRequest).block();
-
-        assertThat(called.get()).isFalse();
-    }
-
-    @Test
-    public void filterShouldReturnUnauthorizedOnInvalidAuthorizationHeader() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(TOKEN);
-
-        assertThatThrownBy(() -> testee.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void filterShouldReturnUnauthorizedOnBadAuthorizationHeader() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn("bad");
-
-        assertThatThrownBy(() -> testee.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void filterShouldReturnUnauthorizedWhenNoStrategy() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(TOKEN);
-
-        Authenticator authFilter = new Authenticator(ImmutableList.of(), new RecordingMetricFactory());
-        assertThatThrownBy(() -> authFilter.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void filterShouldNotThrowOnValidAuthorizationHeader() {
-        AccessToken token = AccessToken.fromString(TOKEN);
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(TOKEN);
-
-        accessTokenRepository.addToken(USERNAME, token).block();
-
-        Authenticator authFilter = Authenticator.of(new RecordingMetricFactory(), ALLOW);
-
-        assertThatCode(() -> authFilter.authenticate(mockedRequest).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    public void filterShouldThrowWhenChainingAuthorizationStrategies() {
-        AccessToken token = AccessToken.fromString(TOKEN);
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(TOKEN);
-
-        accessTokenRepository.addToken(USERNAME, token).block();
-
-        Authenticator authFilter = Authenticator.of(new RecordingMetricFactory(), DENY, ALLOW);
-
-        assertThatThrownBy(() -> authFilter.authenticate(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerTest.java
deleted file mode 100644
index e444f18..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.time.Duration;
-import java.util.function.Predicate;
-
-import org.apache.james.core.Username;
-import org.apache.james.mailbox.DefaultMailboxes;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.store.StoreSubscriptionManager;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.util.concurrency.ConcurrentTestRunner;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.github.fge.lambdas.Throwing;
-
-public class DefaultMailboxesProvisionerTest {
-
-    public static final Username USERNAME = Username.of("username");
-    private DefaultMailboxesProvisioner testee;
-    private MailboxSession session;
-    private InMemoryMailboxManager mailboxManager;
-    private StoreSubscriptionManager subscriptionManager;
-
-    @Before
-    public void before() {
-        session = MailboxSessionUtil.create(USERNAME);
-
-        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
-        subscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory(), mailboxManager.getMapperFactory(), mailboxManager.getEventBus());
-        testee = new DefaultMailboxesProvisioner(mailboxManager, new RecordingMetricFactory());
-    }
-
-    @Test
-    public void createMailboxesIfNeededShouldCreateSystemMailboxes() throws Exception {
-        testee.createMailboxesIfNeeded(session).block();
-
-        assertThat(mailboxManager.list(session))
-            .containsOnly(DefaultMailboxes.DEFAULT_MAILBOXES
-                .stream()
-                .map(mailboxName -> MailboxPath.forUser(USERNAME, mailboxName))
-                .toArray(MailboxPath[]::new));
-    }
-
-    @Test
-    public void createMailboxesIfNeededShouldCreateSpamWhenOtherSystemMailboxesExist() throws Exception {
-        DefaultMailboxes.DEFAULT_MAILBOXES
-            .stream()
-            .filter(Predicate.not(Predicate.isEqual(DefaultMailboxes.SPAM)))
-            .forEach(Throwing.consumer(mailbox -> mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, mailbox), session)));
-
-        testee.createMailboxesIfNeeded(session).block();
-
-        assertThat(mailboxManager.list(session)).contains(MailboxPath.forUser(USERNAME, DefaultMailboxes.SPAM));
-    }
-
-    @Test
-    public void createMailboxesIfNeededShouldSubscribeMailboxes() throws Exception {
-        testee.createMailboxesIfNeeded(session).block();
-
-        assertThat(subscriptionManager.subscriptions(session))
-            .containsOnly(DefaultMailboxes.defaultMailboxesAsPath(USERNAME).toArray(MailboxPath[]::new));
-    }
-
-    @Test
-    public void createMailboxesIfNeededShouldNotGenerateExceptionsInConcurrentEnvironment() throws Exception {
-        ConcurrentTestRunner.builder()
-            .reactorOperation((threadNumber, step) -> testee.createMailboxesIfNeeded(session))
-            .threadCount(10)
-            .runSuccessfullyWithin(Duration.ofSeconds(10));
-
-        assertThat(mailboxManager.list(session))
-            .containsOnly(DefaultMailboxes.defaultMailboxesAsPath(USERNAME).toArray(MailboxPath[]::new));
-    }
-
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerThreadTest.java
deleted file mode 100644
index 6e6724b..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesProvisionerThreadTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.time.Duration;
-
-import org.apache.james.core.Username;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.TestId;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.util.concurrency.ConcurrentTestRunner;
-import org.junit.Before;
-import org.junit.Test;
-
-import reactor.core.publisher.Mono;
-
-public class DefaultMailboxesProvisionerThreadTest {
-
-    private static final Username USERNAME = Username.of("username");
-
-    private DefaultMailboxesProvisioner testee;
-    private MailboxSession session;
-    private MailboxManager mailboxManager;
-
-    @Before
-    public void before() {
-        session = MailboxSessionUtil.create(USERNAME);
-        mailboxManager = mock(MailboxManager.class);
-        when(mailboxManager.createMailboxReactive(any(), any())).thenReturn(Mono.empty());
-        testee = new DefaultMailboxesProvisioner(mailboxManager, new RecordingMetricFactory());
-    }
-
-    @Test
-    public void testConcurrentAccessToFilterShouldNotThrow() throws Exception {
-        when(mailboxManager.createMailboxReactive(any(MailboxPath.class), any(MailboxManager.CreateOption.class), eq(session))).thenReturn(Mono.just(TestId.of(18L)));
-        when(mailboxManager.mailboxExists(any(MailboxPath.class), eq(session))).thenReturn(Mono.just(false));
-        when(mailboxManager.createSystemSession(USERNAME)).thenReturn(session);
-
-        ConcurrentTestRunner
-            .builder()
-            .operation((threadNumber, step) -> testee.createMailboxesIfNeeded(session).block())
-            .threadCount(2)
-            .runSuccessfullyWithin(Duration.ofMinutes(1));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DownloadRoutesTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DownloadRoutesTest.java
deleted file mode 100644
index 78cc977..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DownloadRoutesTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.List;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.api.SimpleTokenFactory;
-import org.apache.james.jmap.draft.exceptions.InternalErrorException;
-import org.apache.james.jmap.methods.BlobManager;
-import org.apache.james.jmap.draft.utils.DownloadPath;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.junit.Test;
-
-import reactor.core.publisher.Mono;
-import reactor.netty.http.server.HttpServerResponse;
-
-public class DownloadRoutesTest {
-
-    @Test
-    public void downloadShouldFailWhenUnknownErrorOnAttachmentManager() {
-        MailboxSession mailboxSession = MailboxSessionUtil.create(Username.of("User"));
-        BlobManager mockedBlobManager = mock(BlobManager.class);
-        when(mockedBlobManager.retrieve(any(List.class), eq(mailboxSession)))
-            .thenReturn(Mono.error(new MailboxException()));
-        Authenticator mockedAuthFilter = mock(Authenticator.class);
-        SimpleTokenFactory nullSimpleTokenFactory = null;
-
-        DownloadRoutes testee = new DownloadRoutes(mockedBlobManager, nullSimpleTokenFactory, new RecordingMetricFactory(), mockedAuthFilter);
-
-        HttpServerResponse resp = mock(HttpServerResponse.class);
-        assertThatThrownBy(() -> testee.download(mailboxSession, DownloadPath.ofBlobId("blobId"), resp).block())
-            .isInstanceOf(InternalErrorException.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java
deleted file mode 100644
index a00ddd8..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static io.restassured.RestAssured.given;
-import static io.restassured.config.EncoderConfig.encoderConfig;
-import static io.restassured.config.RestAssuredConfig.newConfig;
-import static org.apache.james.jmap.JMAPUrls.JMAP;
-import static org.hamcrest.Matchers.equalTo;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.nio.charset.StandardCharsets;
-
-import org.apache.james.core.Username;
-import org.apache.james.jmap.JMAPRoute;
-import org.apache.james.jmap.methods.Method;
-import org.apache.james.jmap.draft.methods.RequestHandler;
-import org.apache.james.jmap.model.InvocationResponse;
-import org.apache.james.jmap.methods.ErrorResponse;
-import org.apache.james.jmap.model.MethodCallId;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import io.netty.handler.codec.http.HttpMethod;
-import io.restassured.RestAssured;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.http.ContentType;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-import reactor.netty.DisposableServer;
-import reactor.netty.http.server.HttpServer;
-
-public class JMAPApiRoutesTest {
-    private static final int RANDOM_PORT = 0;
-
-    private DisposableServer server;
-    private RequestHandler requestHandler;
-    private Authenticator mockedAuthFilter;
-    private UserProvisioner mockedUserProvisionner;
-
-    @Before
-    public void setup() throws Exception {
-        requestHandler = mock(RequestHandler.class);
-        mockedAuthFilter = mock(Authenticator.class);
-        mockedUserProvisionner = mock(UserProvisioner.class);
-
-        JMAPApiRoutes jmapApiRoutes = new JMAPApiRoutes(requestHandler, new RecordingMetricFactory(),
-            mockedAuthFilter, mockedUserProvisionner);
-
-        JMAPRoute postApiRoute = jmapApiRoutes.routes()
-            .filter(jmapRoute -> jmapRoute.getEndpoint().getMethod().equals(HttpMethod.POST))
-            .findFirst()
-            .get();
-
-        server = HttpServer.create()
-            .port(RANDOM_PORT)
-            .route(routes -> routes.post(postApiRoute.getEndpoint().getPath(), (req, res) -> postApiRoute.getAction().handleRequest(req, res)))
-            .bindNow();
-
-        RestAssured.requestSpecification = new RequestSpecBuilder()
-            .setContentType(ContentType.JSON)
-            .setAccept(ContentType.JSON)
-            .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(StandardCharsets.UTF_8)))
-            .setPort(server.port())
-            .setBasePath(JMAP)
-            .build();
-
-        doReturn(Mono.just(MailboxSessionUtil.create(Username.of("bob"))))
-            .when(mockedAuthFilter).authenticate(any());
-        when(mockedUserProvisionner.provisionUser(any()))
-            .thenReturn(Mono.empty());
-    }
-
-    @After
-    public void teardown() {
-        server.disposeNow();
-    }
-
-    @Test
-    public void mustReturnBadRequestOnMalformedRequest() {
-        String missingAnOpeningBracket = "[\"getAccounts\", {\"state\":false}, \"#0\"]]";
-
-        given()
-            .body(missingAnOpeningBracket)
-        .when()
-            .post()
-        .then()
-            .statusCode(400);
-    }
-
-    @Test
-    public void mustReturnInvalidArgumentOnInvalidState() throws Exception {
-        ObjectNode json = new ObjectNode(new JsonNodeFactory(false));
-        json.put("type", "invalidArgument");
-
-        when(requestHandler.handle(any()))
-            .thenReturn(Flux.just(new InvocationResponse(ErrorResponse.ERROR_METHOD, json, MethodCallId.of("#0"))));
-
-        given()
-            .body("[[\"getAccounts\", {\"state\":false}, \"#0\"]]")
-        .when()
-            .post()
-        .then()
-            .statusCode(200)
-            .body(equalTo("[[\"error\",{\"type\":\"invalidArgument\"},\"#0\"]]"));
-    }
-
-    @Test
-    public void mustReturnAccountsOnValidRequest() throws Exception {
-        ObjectNode json = new ObjectNode(new JsonNodeFactory(false));
-        json.put("state", "f6a7e214");
-        ArrayNode arrayNode = json.putArray("list");
-        ObjectNode list = new ObjectNode(new JsonNodeFactory(false));
-        list.put("id", "6asf5");
-        list.put("name", "roger@barcamp");
-        arrayNode.add(list);
-
-        when(requestHandler.handle(any()))
-            .thenReturn(Flux.just(new InvocationResponse(Method.Response.name("accounts"), json, MethodCallId.of("#0"))));
-
-        given()
-            .body("[[\"getAccounts\", {}, \"#0\"]]")
-        .when()
-            .post()
-        .then()
-            .statusCode(200)
-            .body(equalTo("[[\"accounts\",{" +
-                    "\"state\":\"f6a7e214\"," + 
-                    "\"list\":[" + 
-                        "{" + 
-                        "\"id\":\"6asf5\"," + 
-                        "\"name\":\"roger@barcamp\"" + 
-                        "}" + 
-                    "]" + 
-                    "},\"#0\"]]"));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java
deleted file mode 100644
index 9e9f583..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-import java.util.function.Predicate;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.core.Username;
-import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.jwt.JwtTokenVerifier;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.SessionProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.user.memory.MemoryUsersRepository;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import io.netty.handler.codec.http.HttpHeaders;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class JWTAuthenticationStrategyTest {
-    private static final String AUTHORIZATION_HEADERS = "Authorization";
-    private static final DomainList NO_DOMAIN_LIST = null;
-
-    private JWTAuthenticationStrategy testee;
-    private MailboxManager mockedMailboxManager;
-    private JwtTokenVerifier stubTokenVerifier;
-    private HttpServerRequest mockedRequest;
-    private HttpHeaders mockedHeaders;
-
-    @Before
-    public void setup() {
-        stubTokenVerifier = mock(JwtTokenVerifier.class);
-        mockedMailboxManager = mock(MailboxManager.class);
-        mockedRequest = mock(HttpServerRequest.class);
-        mockedHeaders = mock(HttpHeaders.class);
-        MemoryUsersRepository usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
-
-        when(mockedRequest.requestHeaders())
-            .thenReturn(mockedHeaders);
-
-        testee = new JWTAuthenticationStrategy(stubTokenVerifier, mockedMailboxManager, usersRepository);
-    }
-
-
-    @Test
-    public void createMailboxSessionShouldReturnEmptyWhenAuthHeaderIsEmpty() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn("");
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenAuthHeadersIsInvalid() {
-        String username = "123456789";
-        String validAuthHeader = "valid";
-        String fakeAuthHeaderWithPrefix = JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX + validAuthHeader;
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-
-        when(stubTokenVerifier.verifyAndExtractLogin(validAuthHeader)).thenReturn(Optional.empty());
-        when(mockedMailboxManager.createSystemSession(eq(Username.of(username))))
-                .thenReturn(fakeMailboxSession);
-
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(fakeAuthHeaderWithPrefix);
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).blockOptional())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnEmptyWhenAuthHeaderIsInvalid() {
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn("bad");
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenMultipleHeaders() {
-        String authHeader1 = JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX + "token1";
-        String authHeader2 = JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX + "token2";
-
-        when(mockedHeaders.getAll(AUTHORIZATION_HEADERS))
-            .thenReturn(ImmutableList.of(authHeader1, authHeader2));
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).blockOptional())
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnWhenAuthHeadersAreValid() {
-        String username = "123456789";
-        String validAuthHeader = "valid";
-        String fakeAuthHeaderWithPrefix = JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX + validAuthHeader;
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-
-        when(stubTokenVerifier.verifyAndExtractLogin(validAuthHeader)).thenReturn(Optional.of(username));
-        when(mockedMailboxManager.authenticate(eq(Username.of(username))))
-            .thenReturn(new SessionProvider.AuthorizationStep() {
-                @Override
-                public MailboxSession as(Username other) throws MailboxException {
-                    throw new NotImplementedException();
-                }
-
-                @Override
-                public MailboxSession withoutDelegation() throws MailboxException {
-                    return fakeMailboxSession;
-                }
-
-                @Override
-                public MailboxSession forMatchingUser(Predicate<Username> other) throws MailboxException {
-                    throw new NotImplementedException();
-                }
-            });
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(fakeAuthHeaderWithPrefix);
-
-        MailboxSession result = testee.createMailboxSession(mockedRequest).block();
-        assertThat(result).isEqualTo(fakeMailboxSession);
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowUponInvalidVirtualHosting() {
-        String username = "123456789@domain.tld";
-        String validAuthHeader = "valid";
-        String fakeAuthHeaderWithPrefix = JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX + validAuthHeader;
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-
-        when(stubTokenVerifier.verifyAndExtractLogin(validAuthHeader)).thenReturn(Optional.of(username));
-        when(mockedMailboxManager.createSystemSession(eq(Username.of(username))))
-                .thenReturn(fakeMailboxSession);
-        when(mockedHeaders.get(AUTHORIZATION_HEADERS))
-            .thenReturn(fakeAuthHeaderWithPrefix);
-
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).block())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategyTest.java
deleted file mode 100644
index 3895c58..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/QueryParameterAccessTokenAuthenticationStrategyTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.apache.james.jmap.draft.api.SimpleTokenManager;
-import org.apache.james.mailbox.MailboxManager;
-import org.junit.Before;
-import org.junit.Test;
-
-import reactor.netty.http.server.HttpServerRequest;
-
-public class QueryParameterAccessTokenAuthenticationStrategyTest {
-
-    private QueryParameterAccessTokenAuthenticationStrategy testee;
-    private HttpServerRequest mockedRequest;
-
-    @Before
-    public void setup() {
-        SimpleTokenManager mockedSimpleTokenManager = mock(SimpleTokenManager.class);
-        MailboxManager mockedMailboxManager = mock(MailboxManager.class);
-        mockedRequest = mock(HttpServerRequest.class);
-
-        testee = new QueryParameterAccessTokenAuthenticationStrategy(mockedSimpleTokenManager, mockedMailboxManager);
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenNoAccessTokenProvided() {
-        when(mockedRequest.param("access_token"))
-            .thenReturn(null);
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-    @Test
-    public void createMailboxSessionShouldThrowWhenAccessTokenIsNotValid() {
-        when(mockedRequest.param("access_token"))
-            .thenReturn("bad");
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-}
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java
deleted file mode 100644
index 876dfad..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import org.apache.james.core.Username;
-import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.user.api.UsersRepository;
-import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.james.user.memory.MemoryUsersRepository;
-import org.junit.Before;
-import org.junit.Test;
-
-public class UserProvisionerTest {
-    private static final Username USERNAME = Username.of("username");
-    private static final Username USERNAME_WITH_DOMAIN = Username.of("username@james.org");
-    private static final DomainList NO_DOMAIN_LIST = null;
-
-    private UserProvisioner testee;
-    private MemoryUsersRepository usersRepository;
-
-    @Before
-    public void setup() throws Exception {
-        usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
-        testee = new UserProvisioner(JMAPConfiguration.DEFAULT, usersRepository, new RecordingMetricFactory());
-    }
-
-    @Test
-    public void filterShouldDoNothingOnNullSession() throws UsersRepositoryException {
-        testee.provisionUser(null).block();
-
-        assertThat(usersRepository.list()).toIterable()
-            .isEmpty();
-    }
-
-    @Test
-    public void filterShouldAddUsernameWhenNoVirtualHostingAndMailboxSessionContainsUsername() throws UsersRepositoryException {
-        usersRepository.setEnableVirtualHosting(false);
-        MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME);
-
-        testee.provisionUser(mailboxSession).block();
-
-        assertThat(usersRepository.list()).toIterable()
-            .contains(USERNAME);
-    }
-
-    @Test
-    public void filterShouldFailOnInvalidVirtualHosting() {
-        usersRepository.setEnableVirtualHosting(false);
-        MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN);
-
-        assertThatThrownBy(() -> testee.provisionUser(mailboxSession).block())
-            .hasCauseInstanceOf(UsersRepositoryException.class);
-    }
-
-    @Test
-    public void filterShouldNotTryToAddUserWhenReadOnlyUsersRepository() {
-        UsersRepository usersRepository = mock(UsersRepository.class);
-        when(usersRepository.isReadOnly()).thenReturn(true);
-        testee = new UserProvisioner(JMAPConfiguration.DEFAULT, usersRepository, new RecordingMetricFactory());
-
-        MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN);
-
-        testee.provisionUser(mailboxSession).block();
-
-        verify(usersRepository).isReadOnly();
-        verifyNoMoreInteractions(usersRepository);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java
deleted file mode 100644
index 4d3c257..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import java.time.Duration;
-import java.util.concurrent.ExecutionException;
-
-import org.apache.james.core.Username;
-import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.jmap.JMAPConfiguration;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.metrics.tests.RecordingMetricFactory;
-import org.apache.james.user.memory.MemoryUsersRepository;
-import org.apache.james.util.concurrency.ConcurrentTestRunner;
-import org.junit.Before;
-import org.junit.Test;
-
-public class UserProvisionerThreadTest {
-    private static final DomainList NO_DOMAIN_LIST = null;
-
-    private UserProvisioner testee;
-    private MemoryUsersRepository usersRepository;
-    private MailboxSession session;
-
-    @Before
-    public void before() {
-        usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
-        session = MailboxSessionUtil.create(Username.of("username"));
-        testee = new UserProvisioner(JMAPConfiguration.DEFAULT, usersRepository, new RecordingMetricFactory());
-    }
-
-    @Test
-    public void testConcurrentAccessToFilterShouldNotThrow() throws ExecutionException, InterruptedException {
-        ConcurrentTestRunner
-            .builder()
-            .operation((threadNumber, step) -> testee.provisionUser(session))
-            .threadCount(2)
-            .runSuccessfullyWithin(Duration.ofMinutes(1));
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/XUserAuthenticationStrategyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/XUserAuthenticationStrategyTest.java
deleted file mode 100644
index d837465..0000000
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/XUserAuthenticationStrategyTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.jmap.http;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.function.Predicate;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.apache.james.core.Username;
-import org.apache.james.dnsservice.api.DNSService;
-import org.apache.james.domainlist.lib.DomainListConfiguration;
-import org.apache.james.domainlist.memory.MemoryDomainList;
-import org.apache.james.jmap.exceptions.UnauthorizedException;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.SessionProvider;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.user.memory.MemoryUsersRepository;
-import org.junit.Before;
-import org.junit.Test;
-
-import io.netty.handler.codec.http.HttpHeaders;
-import reactor.netty.http.server.HttpServerRequest;
-
-public class XUserAuthenticationStrategyTest {
-    private XUserAuthenticationStrategy testee;
-    private HttpServerRequest mockedRequest;
-    private HttpHeaders mockedHeaders;
-
-    @Before
-    public void setup() throws Exception {
-        MailboxManager mockedMailboxManager = mock(MailboxManager.class);
-        mockedRequest = mock(HttpServerRequest.class);
-        mockedHeaders = mock(HttpHeaders.class);
-
-        DNSService dnsService = mock(DNSService.class);
-        MemoryDomainList domainList = new MemoryDomainList(dnsService);
-        domainList.configure(DomainListConfiguration.DEFAULT);
-        MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
-
-        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
-        when(mockedMailboxManager.createSystemSession(any()))
-            .thenReturn(fakeMailboxSession);
-
-        when(mockedMailboxManager.authenticate(any()))
-            .thenReturn(new SessionProvider.AuthorizationStep() {
-                @Override
-                public MailboxSession as(Username other) {
-                    throw new NotImplementedException();
-                }
-
-                @Override
-                public MailboxSession withoutDelegation() {
-                    return fakeMailboxSession;
-                }
-
-                @Override
-                public MailboxSession forMatchingUser(Predicate<Username> other) throws MailboxException {
-                    throw new NotImplementedException();
-                }
-            });
-
-        when(mockedRequest.requestHeaders())
-            .thenReturn(mockedHeaders);
-
-        testee = new XUserAuthenticationStrategy(usersRepository, mockedMailboxManager);
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnEmptyWhenHeaderIsEmpty() {
-        when(mockedHeaders.get("X-User"))
-            .thenReturn("");
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-    @Test
-    public void createMailboxSessionShouldReturnEmptyWhenHeaderIsNull() {
-        when(mockedHeaders.get("X-User"))
-            .thenReturn(null);
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .isEmpty();
-    }
-
-
-    @Test
-    public void createMailboxSessionShouldFailWhenInvalidUser() {
-        when(mockedHeaders.get("X-User"))
-            .thenReturn("btellier"); // invalid because virtual hosting is turned on
-
-        assertThatThrownBy(() -> testee.createMailboxSession(mockedRequest).blockOptional())
-            .isInstanceOf(UnauthorizedException.class);
-    }
-
-
-    @Test
-    public void createMailboxSessionShouldReturnSessionWhenValid() {
-        when(mockedHeaders.get("X-User"))
-            .thenReturn("btellier@localhost");
-
-        assertThat(testee.createMailboxSession(mockedRequest).blockOptional())
-            .get()
-            .isInstanceOf(MailboxSession.class);
-    }
-}
diff --git a/server/protocols/jmap-draft/src/test/resources/badAliasKeystore b/server/protocols/jmap-draft/src/test/resources/badAliasKeystore
deleted file mode 100644
index 0a4de22..0000000
--- a/server/protocols/jmap-draft/src/test/resources/badAliasKeystore
+++ /dev/null
Binary files differ
diff --git a/server/protocols/jmap-draft/src/test/resources/calendar.eml b/server/protocols/jmap-draft/src/test/resources/calendar.eml
deleted file mode 100644
index 2ae4533..0000000
--- a/server/protocols/jmap-draft/src/test/resources/calendar.eml
+++ /dev/null
@@ -1,42 +0,0 @@
-Return-Path: <local@localhost.com>
-MIME-Version: 1.0
-Delivered-To: to@localhost.com
-From: User From <fromUser@localhost.com>
-Message-ID: <2d0fa16c-4c37-58de-1acc-50b5f6a23a60@localhost.com>
-To: User To <toUser@linagora.com>
-Date: Wed, 26 Jul 2017 10:34:02 +0200
-Subject: Any subject
-Content-class: urn:content-classes:calendarmessage
-Content-Type: text/calendar; method=REPLY; charset=UTF-8
-Content-transfer-encoding: 8BIT
-
-BEGIN:VCALENDAR
-PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
-VERSION:2.0
-METHOD:REPLY
-BEGIN:VEVENT
-CREATED:20170726T081145Z
-LAST-MODIFIED:20170726T083402Z
-DTSTAMP:20170726T083402Z
-UID:f1514f44bf39311568d6407249cb76c48103fcd1fb398c3c501cb72b2d293f36e04
- b2aab16e43439a608f28671ab7c10e754cbc85ddee4a07fa8cf8f4f7af05bfb502b8f6
-
-SUMMARY:[A CONFIRMER] Call OVH
-PRIORITY:5
-ORGANIZER;CN=User;X-OBM-ID=id:mailto:userTo@localhost.com
-ATTENDEE;CN=User;PARTSTAT=ACCEPTED;CUTYPE=INDIVIDUAL;X-OBM-ID=810:
- mailto:user1To@linagora.com
-DTSTART:20170727T130000Z
-DURATION:PT1H
-TRANSP:OPAQUE
-SEQUENCE:2
-X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR:No value for X property. Rem
- oving entire property:
-CLASS:PUBLIC
-X-OBM-DOMAIN:linagora.com
-X-OBM-DOMAIN-UUID:uuid_value
-LOCATION:01.78.14.42.61 / 1586
-X-OBM-ALERT;X-OBM-ID=486:600
-END:VEVENT
-END:VCALENDAR
-
diff --git a/server/protocols/jmap-draft/src/test/resources/emptyBodyMessageWithOneAttachment.eml b/server/protocols/jmap-draft/src/test/resources/emptyBodyMessageWithOneAttachment.eml
deleted file mode 100644
index 125151c..0000000
--- a/server/protocols/jmap-draft/src/test/resources/emptyBodyMessageWithOneAttachment.eml
+++ /dev/null
@@ -1,52 +0,0 @@
-Reply-to: alice <alice@local>
-In-reply-to: bob@local
-Mime-Version: 1.0
-To: bob <bob@local>
-From: alice <alice@local>
-Cc: jack <jack@local>, jacob <jacob@local>
-Bcc: alice <alice@local>
-Subject: Full message
-Message-ID: <1cc7f114-dbc4-42c2-99bd-f1100db6d0c1@open-paas.org>
-Date: Tue, 7 Jun 2016 16:23:37 +0200
-Content-Type: multipart/mixed;
- boundary="------------7AF1D14DE1DFA16229726B54"
-
-This is a multi-part message in MIME format.
---------------7AF1D14DE1DFA16229726B54
-Content-Type: multipart/alternative;
- boundary="------------172F9470CFA3BF3417835D92"
-
-
---------------172F9470CFA3BF3417835D92
-Content-Type: text/plain; charset=utf-8; format=flowed
-Content-Transfer-Encoding: 7bit
-
-
-
---------------172F9470CFA3BF3417835D92--
-
---------------7AF1D14DE1DFA16229726B54
-Content-Type: image/jpeg;
- name="4037_014.jpg"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="4037_014.jpg"
-
-/9j/4X2cRXhpZgAASUkqAAgAAAANAA8BAgAKAAAAqgAAABABAgAJAAAAtAAAABIBAwABAAAA
-AQAAABoBBQABAAAAvgAAABsBBQABAAAAxgAAACgBAwABAAAAAgAAADEBAgAKAAAAzgAAADIB
-AgAUAAAA2AAAABMCAwABAAAAAgAAAGmHBAABAAAAfAIAAKXEBwDQAAAA7AAAANLGBwBAAAAA
-vAEAANPGBwCAAAAA/AEAAEwqAABQYW5hc29uaWMARE1DLUZaNDUAALQAAAABAAAAtAAAAAEA
-AABWZXIuMS4wICAAMjAxNDowMjoyNSAxMDozMjowOQBQcmludElNADAyNTAAAA4AAQAWABYA
-AgAAAAAAAwBkAAAABwAAAAAACAAAAAAACQAAAAAACgAAAAAACwCsAAAADAAAAAAADQAAAAAA
-DgDEAAAAAAEFAAAAAQEBAAAAEAGAAAAACREAABAnAAALDwAAECcAAJcFAAAQJwAAsAgAABAn
-AAABHAAAECcAAF4CAAAQJwAAiwAAABAnAADLAwAAECcAAOUbAAAQJwAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-
---------------7AF1D14DE1DFA16229726B54--
diff --git a/server/protocols/jmap-draft/src/test/resources/example.html b/server/protocols/jmap-draft/src/test/resources/example.html
deleted file mode 100644
index 59d3395..0000000
--- a/server/protocols/jmap-draft/src/test/resources/example.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="section">
-    <h3>Why a new Logo?<a name="Why_a_new_Logo"></a></h3>
-
-
-    <p>We are happy with our current logo, but for the
-        upcoming James Server 3.0 release, we would like to
-        give our community the opportunity to create a new image for James.</p>
-
-
-    <p>Don't be shy, take your inkscape and gimp, and send us on
-        the <a href="mail.html">James Server User mailing list</a>
-        your creations. We will publish them on this page.</p>
-
-
-    <p>We need an horizontal logo (100p height) to be show displayed on the upper
-        left corner of this page, an avatar (48x48p) to be used on a Twitter stream for example.
-        The used fonts should be redistributable (or commonly available on Windows and Linux).
-        The chosen logo should be delivered in SVG format.
-        We also like the <a class="externalLink" href="http://www.apache.org/foundation/press/kit/">Apache feather</a>.</p>
-
-</div>
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/resources/fullMessage.eml b/server/protocols/jmap-draft/src/test/resources/fullMessage.eml
deleted file mode 100644
index daf28d3..0000000
--- a/server/protocols/jmap-draft/src/test/resources/fullMessage.eml
+++ /dev/null
@@ -1,60 +0,0 @@
-Reply-to: alice <alice@local>
-In-reply-to: bob@local
-Mime-Version: 1.0
-To: bob <bob@local>
-From: alice <alice@local>
-Cc: jack <jack@local>, jacob <jacob@local>
-Bcc: alice <alice@local>
-Subject: Full message
-Message-ID: <1cc7f114-dbc4-42c2-99bd-f1100db6d0c1@open-paas.org>
-Date: Tue, 7 Jun 2016 16:23:37 +0200
-Content-Type: multipart/mixed;
- boundary="------------7AF1D14DE1DFA16229726B54"
-
-This is a multi-part message in MIME format.
---------------7AF1D14DE1DFA16229726B54
-Content-Type: multipart/alternative;
- boundary="------------172F9470CFA3BF3417835D92"
-
-
---------------172F9470CFA3BF3417835D92
-Content-Type: text/plain; charset=utf-8; format=flowed
-Content-Transfer-Encoding: 7bit
-
-/blabla/
-*bloblo*
-
---------------172F9470CFA3BF3417835D92
-Content-Type: text/html; charset=utf-8
-Content-Transfer-Encoding: 7bit
-
-<i>blabla</i>
-<b>bloblo</b>
-
---------------172F9470CFA3BF3417835D92--
-
---------------7AF1D14DE1DFA16229726B54
-Content-Type: image/jpeg;
- name="4037_014.jpg"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="4037_014.jpg"
-
-/9j/4X2cRXhpZgAASUkqAAgAAAANAA8BAgAKAAAAqgAAABABAgAJAAAAtAAAABIBAwABAAAA
-AQAAABoBBQABAAAAvgAAABsBBQABAAAAxgAAACgBAwABAAAAAgAAADEBAgAKAAAAzgAAADIB
-AgAUAAAA2AAAABMCAwABAAAAAgAAAGmHBAABAAAAfAIAAKXEBwDQAAAA7AAAANLGBwBAAAAA
-vAEAANPGBwCAAAAA/AEAAEwqAABQYW5hc29uaWMARE1DLUZaNDUAALQAAAABAAAAtAAAAAEA
-AABWZXIuMS4wICAAMjAxNDowMjoyNSAxMDozMjowOQBQcmludElNADAyNTAAAA4AAQAWABYA
-AgAAAAAAAwBkAAAABwAAAAAACAAAAAAACQAAAAAACgAAAAAACwCsAAAADAAAAAAADQAAAAAA
-DgDEAAAAAAEFAAAAAQEBAAAAEAGAAAAACREAABAnAAALDwAAECcAAJcFAAAQJwAAsAgAABAn
-AAABHAAAECcAAF4CAAAQJwAAiwAAABAnAADLAwAAECcAAOUbAAAQJwAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-
---------------7AF1D14DE1DFA16229726B54--
diff --git a/server/protocols/jmap-draft/src/test/resources/inlineAttachment.eml b/server/protocols/jmap-draft/src/test/resources/inlineAttachment.eml
deleted file mode 100644
index 79e4c11..0000000
--- a/server/protocols/jmap-draft/src/test/resources/inlineAttachment.eml
+++ /dev/null
@@ -1,24 +0,0 @@
-To: me@linagora.com
-From: Benoit Tellier <me@linagora.com>
-Subject: Mail with text attachment
-Message-ID: <befd8cab-9c9c-5537-4e77-937f32326087@any.com>
-Date: Thu, 13 Oct 2016 10:26:20 +0200
-MIME-Version: 1.0
-Content-Type: multipart/mixed;
- boundary="------------4FD2D252DB453546C22C25B2"
-
-This is a multi-part message in MIME format.
---------------4FD2D252DB453546C22C25B2
-Content-Type: text/plain; charset=utf-8
-Content-Transfer-Encoding: 7bit
-
-I'm the body!
-
---------------4FD2D252DB453546C22C25B2
-Content-Disposition: inline
-Content-Type: text/plain; charset=UTF-8;
- name="attachment.txt"
-Content-ID: <id>
-
-I'm a Schroedinger cat
---------------4FD2D252DB453546C22C25B2--
diff --git a/server/protocols/jmap-draft/src/test/resources/json/message.json b/server/protocols/jmap-draft/src/test/resources/json/message.json
deleted file mode 100644
index 264c6fb..0000000
--- a/server/protocols/jmap-draft/src/test/resources/json/message.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "id": "1",
-    "blobId": "myBlobId",
-    "threadId": "myThreadId",
-    "mailboxIds": ["1", "2"],
-    "inReplyToMessageId": "myInReplyToMessageId",
-    "isUnread": false,
-    "isFlagged": true,
-    "isAnswered": true,
-    "isDraft": true,
-    "isForwarded": true,
-    "hasAttachment": false,
-    "headers": { "h1": "h1Value", "h2": "h2Value" },
-    "from": { "name": "myName", "email": "myEmail@james.org"},
-    "to": [ { "name": "to1", "email": "to1@james.org"}, { "name": "to2", "email": "to2@james.org"} ],
-    "cc": [ { "name": "cc1", "email": "cc1@james.org"}, { "name": "cc2", "email": "cc2@james.org"} ],
-    "bcc": [ { "name": "bcc1", "email": "bcc1@james.org"}, { "name": "bcc2", "email": "bcc2@james.org"} ],
-    "replyTo": [ { "name": "replyTo1", "email": "replyTo1@james.org"}, { "name": "replyTo2", "email": "replyTo2@james.org"} ],
-    "subject": "mySubject",
-    "date": "2014-10-30T14:12:00Z",
-    "size": 1024,
-    "preview": "myPreview",
-    "textBody": "myTextBody",
-    "htmlBody": "<h1>myHtmlBody</h1>",
-    "attachments": [ ],
-    "attachedMessages": { },
-    "keywords": {"$Draft" : true, "$Flagged" : true, "$Answered" : true, "$Seen" : true, "$Forwarded" : true}
-}
diff --git a/server/protocols/jmap-draft/src/test/resources/json/subMessage.json b/server/protocols/jmap-draft/src/test/resources/json/subMessage.json
deleted file mode 100644
index a5769e4..0000000
--- a/server/protocols/jmap-draft/src/test/resources/json/subMessage.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "headers": { "h1": "h1Value", "h2": "h2Value" },
-    "from": { "name": "myName", "email": "myEmail@james.org"},
-    "to": [ { "name": "to1", "email": "to1@james.org"}, { "name": "to2", "email": "to2@james.org"} ],
-    "cc": [ { "name": "cc1", "email": "cc1@james.org"}, { "name": "cc2", "email": "cc2@james.org"} ],
-    "bcc": [ { "name": "bcc1", "email": "bcc1@james.org"}, { "name": "bcc2", "email": "bcc2@james.org"} ],
-    "replyTo": [ { "name": "replyTo1", "email": "replyTo1@james.org"}, { "name": "replyTo2", "email": "replyTo2@james.org"} ],
-    "subject": "mySubject",
-    "date": "2014-10-30T14:12:00Z",
-    "textBody": "myTextBody",
-    "htmlBody": "<h1>myHtmlBody</h1>",
-    "attachments": [ ],
-    "attachedMessages": { }
-}
diff --git a/server/protocols/jmap-draft/src/test/resources/key.pub b/server/protocols/jmap-draft/src/test/resources/key.pub
deleted file mode 100644
index e0fcd09..0000000
--- a/server/protocols/jmap-draft/src/test/resources/key.pub
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzpnC3fUkdKJLEg9ussLh
-9juskI7NVyGjwtQwV5+qMdigDwxW3GoUG02wz9Uo5wyZcyM1c8eNQX1Xnyd+muER
-/ARGott3+7iNxrPylev0kfzIRP216NwyZn5vrlhQ3JwYhpumwXAL+q04MKVesOVa
-QQWl7H7hfMm6NBwaUrsxjFzOYYIZUALYb/T3hgkaiMm5cm+iSCA5eFszTGGvYmgA
-eQiKs6j2A9YCIOs3dlo1sMBOx/JAiHHuZHjVh1recW8rcX3iLGq8P/le1rpNZ3Lv
-nDzqAp2lq4Q3hlaB+fBcl5JB+zIY4uV4wdh+oopj2djrrwwzxmzskriEAjX9BbDY
-9QIDAQAB
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/server/protocols/jmap-draft/src/test/resources/keystore b/server/protocols/jmap-draft/src/test/resources/keystore
deleted file mode 100644
index 536a6c7..0000000
--- a/server/protocols/jmap-draft/src/test/resources/keystore
+++ /dev/null
Binary files differ
diff --git a/server/protocols/jmap-draft/src/test/resources/keystoreJava11 b/server/protocols/jmap-draft/src/test/resources/keystoreJava11
deleted file mode 100644
index e4492c3..0000000
--- a/server/protocols/jmap-draft/src/test/resources/keystoreJava11
+++ /dev/null
Binary files differ
diff --git a/server/protocols/jmap-draft/src/test/resources/keystoreJava7 b/server/protocols/jmap-draft/src/test/resources/keystoreJava7
deleted file mode 100644
index aa45ad0..0000000
--- a/server/protocols/jmap-draft/src/test/resources/keystoreJava7
+++ /dev/null
Binary files differ
diff --git a/server/protocols/jmap-draft/src/test/resources/noReplyTo.eml b/server/protocols/jmap-draft/src/test/resources/noReplyTo.eml
deleted file mode 100644
index d4f0c3f..0000000
--- a/server/protocols/jmap-draft/src/test/resources/noReplyTo.eml
+++ /dev/null
@@ -1,69 +0,0 @@
-Return-Path: <root-bounces@listes.apache.org>
-Content-Type: multipart/mixed; boundary="----------=_1433322346-12583-0"
-MIME-Version: 1.0
-From: "Content-filter at spam.apache.org" <postmaster@apache.org>
-Date: Wed, 3 Jun 2015 09:05:46 +0000 (UTC)
-To: <root@listes.apache.org>
-Message-ID: <VASs-IZaXqmZao@spam.apache.org>
-Subject: Original subject
-X-BeenThere: root@listes.apache.org
-Errors-To: root-bounces@listes.apache.org
-Sender: "root" <root-bounces@listes.apache.org>
-Reply-To: <>
-
-This is a multi-part message in MIME format...
-
-------------=_1433322346-12583-0
-Content-Type: text/plain; charset="UTF-8"
-Content-Disposition: inline
-Content-Transfer-Encoding: 7bit
-
-No viruses were found.
-
-Content type: Unchecked
-Internal reference code for the message is 12583-16/Ss-IZaXqmZao
-
-According to a 'Received:' trace, the message apparently originated at:
-  [198.252.153.129], [127.0.0.1] localhost [127.0.0.1] Authenticated sender:
-  quentin.h
-
-Return-Path: <quentin.h@apache.org>
-From: Quentin <quentin.h@apache.org>
-Message-ID: <556EC361.6000308@apache.org>
-Subject: XYZ
-Not quarantined.
-
-The message WILL BE relayed to:
-<xxx@apache.org>
-
-
-------------=_1433322346-12583-0
-Content-Type: text/rfc822-headers; name="header"
-Content-Disposition: inline; filename="header"
-Content-Transfer-Encoding: 7bit
-Content-Description: Message header section
-
-Return-Path: <quentin.h@apache.org>
-Message-ID: <556EC361.6000308@apache.org>
-Date: Wed, 03 Jun 2015 11:05:37 +0200
-From: Quentin <quentin.h@apache.org>
-MIME-Version: 1.0
-To: Yann <yann@apache.org>
-Subject: XYZ
-References: <556E332B.8020808@wootdevices.io> <556E3512.3050404@apache.org>
-In-Reply-To: <556E3512.3050404@apache.org>
-Content-Type: multipart/encrypted;
- protocol="application/pgp-encrypted";
- boundary="M0xVhKIvXqi85dG57o5RfCUAoFwhAw1Nh"
-
-------------=_1433322346-12583-0
-Content-Type: text/plain; charset="iso-8859-1"
-MIME-Version: 1.0
-Content-Transfer-Encoding: quoted-printable
-Content-Disposition: inline
-
-_______________________________________________
-root mailing list
-root@listes.apache.org
-https://listes.apache.org/cgi-bin/mailman/listinfo/root
-------------=_1433322346-12583-0--
diff --git a/server/protocols/jmap-draft/src/test/resources/oneAttachment.eml b/server/protocols/jmap-draft/src/test/resources/oneAttachment.eml
deleted file mode 100644
index 3c422f9..0000000
--- a/server/protocols/jmap-draft/src/test/resources/oneAttachment.eml
+++ /dev/null
@@ -1,16 +0,0 @@
-Return-Path: <chibenwa@apache.org>
-Subject: 29989 btellier
-From: <any@mail.com>
-Content-Disposition: attachment
-MIME-Version: 1.0
-Date: Sun, 02 Apr 2017 22:09:04 -0000
-Content-Type: application/zip; name="9559333830.zip"
-To: <chibenwa@apache.org>
-Message-ID: <149117094410.10639.6001033367375624@any.com>
-Content-Transfer-Encoding: base64
-
-UEsDBBQAAgAIAEQeg0oN2YT/EAsAAMsWAAAIABwAMjIwODUuanNVVAkAAxBy4VgQcuFYdXgLAAEE
-AAAAAAQAAAAApZhbi1zHFYWfY/B/MP3i7kwj1/2CokAwBPIQ+sGPkgJ1tURkdeiMbYzQf8+3q8+M
-ZmQllgn2aHrqnNq1L2uvtavnj2/b7evz26/Op5M6q/P+8OUX77784g8/lQtLisXTU/68vfzCv/Lg
-D9vqs/3b8fNXf92273ey4XTCykk9w9LpfD7tX+zGzU83b8pPg39uBr/Kmxe7w9PLuP3xwpFKTJ32
-AAEEAAAAAAQAAAAAUEsFBgAAAAABAAEATgAAAFILAAAAAA==
diff --git a/server/protocols/jmap-draft/src/test/resources/private.nopass.key b/server/protocols/jmap-draft/src/test/resources/private.nopass.key
deleted file mode 100644
index ad7c2e0..0000000
--- a/server/protocols/jmap-draft/src/test/resources/private.nopass.key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAsjjah2w8AKpnGKya4QG/tdRoR9pJkKWfyf17ywcYWBxcpj0Q
-+dkn+CXvBafhQ2zlf+bPkWxYhBuXyMB8QNCp/sMlaQ8dGFw/LGojglHk8T4aIu+a
-Ffy0hgN9yniuEHmFdjP2XECbA7UbHQPZTO/DU3QJ0FabqKO61pHB4bliNsTWGjzg
-seU5kdS1Uup0AK/URO2pSLpnDPV/l0yNmxvGfO/ulPNVJyxiJuT+Rl51LlxpWhu3
-G/hFX2mJP0Mn/cX3xNm2HrYIaasglum7bXN/vfiqSFAg46LgT7UJ4pCHoLI99K7Z
-OpgBqK/Q4P4UxOggxkawI+JmVLTmCpz1c6JR3QIDAQABAoIBAEvx7DVfCuahtpMO
-ImWVkDMUk2di/owz97ZkXRc0K8yIMXbpeM1vGxgkWgmH83b+9YEeQv9OMx4AgBqc
-45XfFUGpuMxfE1/pF0Z+6CjFaxDeDNcsUPXjnK2q2TSpXktI7XVkSSwgn63hw+8G
-T9f4J7ztKq1r6qLfOmPq8rqUe+SoH5tkwmJJgnxx3Qt2xZ4nmFXiuscOcVUjV3D/
-EdghjcExNSgxmo8HN34DLQPZrDDwarrYJouiufe7m0Etk8m9bOWkNsoCif0wQ0mS
-uQVAHMzCKqrUmAM3X1IimTnwOWu2sYle9VXYijwDkZ0PGQnS4rywz9D9MWy57ZLM
-zklb5MUCgYEA5qrK9PmMGNi6ihhjkDw6b4sqOs4Dec89jdkhhYdrbPVXZzB+Shmh
-CDhviXhLi7m5G5d707DLPloPR0LdNmf3YsLCvMej70O8DrDG/z1Vv++DF2XcAtvW
-TLoXOcSS1E3tKRSp2Uks7osWIwUAomhkEIXsDJ+esH9wHD7rOYA02fsCgYEAxcuR
-Sp0rcVqwyuINEmrvH5zxSWk0O5MqM95kyvElFuPCPAKv0EK9t8T7dv7fEtQoupqa
-hbDGzatif7B4tVaeWZRYJUrr9uwIyZfVSfYUi5y2KwHGJJPH+EkoQ6sZlRw6Aqj/
-AeW4/cnmhK+IrgBDKF99DiVs1nVTgeA0vEneVAcCgYBh9l0VpznJf5nRdkQqiHmk
-z6ySptWxEMOqj3F+HSIRdyy0hVLNM49BR36PGoizAaBMyIvtPqNCisr8T7hVF9hn
-mCFOPVcxEUo3zvnkgm79gcHh2P+UMzsTAOFGRma5RZMYJE9yGXK8qFMxcjlvv/fn
-UIkMX2nsuWKqjEon9Hv9qwKBgFLk760/lVdaVMGSXzwayMCM+6pyhst477TxPXi3
-+31OUrbnwVscvXno+iJyynaPp8ocxSnGXmsPqqYtBUmqVY7hVte6QI4peb1S/raS
-hSXFtarsgWgO7z2lG1TQvlX5kG5Q+R6IcjEajpuJHVUfEwY2jqiBEs4GOcsydad7
-pcu9AoGBANHQQS7U1o0Ks2H9iB02b72rq0Lr7d4xJ7bbeL0PdJNxnKWYtYYrNKvu
-R7wWF6MBH+pfm0CzJo0Pog58wkAiKnD8nPuVUdXgi/3PQXu/ZARY0g30wqubaBeZ
-UN7bL/TrIc67ldEi07A8Wo6H5Gd6jIHBnmO93an2zXlrvhrvACRX
------END RSA PRIVATE KEY-----
diff --git a/server/protocols/jmap-draft/src/test/resources/spamMail.eml b/server/protocols/jmap-draft/src/test/resources/spamMail.eml
deleted file mode 100644
index 8121d99..0000000
--- a/server/protocols/jmap-draft/src/test/resources/spamMail.eml
+++ /dev/null
@@ -1,135 +0,0 @@
-Return-Path: <root-bounces@listes.minet.net>
-Received: from mx1.minet.net (mx1.minet.net [192.168.102.25])
-	 by imap (Cyrus v2.4.16-Debian-2.4.16-4+deb7u1) with LMTPA;
-	 Wed, 03 Jun 2015 11:05:45 +0200
-X-Sieve: CMU Sieve 2.4
-Received: from smtp.minet.net (smtp.minet.net [192.168.102.18])
-	by mx1.minet.net (Postfix) with ESMTP id F06FE610F72;
-	Wed,  3 Jun 2015 11:05:48 +0200 (CEST)
-Received: from listes.minet.net (listes.minet.net [192.168.102.29])
-	by smtp.minet.net (Postfix) with ESMTP id AAB15610F70;
-	Wed,  3 Jun 2015 09:05:48 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=minet.net; s=mail;
-	t=1433322348; bh=8OtmVRMX7IWWyQo8WQIi5rKeDEQFghSX2r1WN9/pqUk=;
-	h=From:Date:To:Subject:List-Id:List-Unsubscribe:List-Post:List-Help:
-	 List-Subscribe:From;
-	b=UkthSFXn5mtPnn7sYvDJIxgCk1O36L0p+hTOnI/xTF2WRMGJyXtLyCdFdE5jQ5gE4
-	 EGNdhwmfjaKsoE7IDtc/4GrPvbnM8i9ojpSOBW1hXJPOeYnRgah/4gsXsMJWvmmq/A
-	 4oTnw8ZswFXiShDmBMyaSEZuVrWTdaPBgUhVhGw8=
-Received: from listes.minet.net (listes.minet.net [127.0.0.1])
-	by listes.minet.net (Postfix) with ESMTP id 9CD5BA27FF9;
-	Wed,  3 Jun 2015 09:05:48 +0000 (UTC)
-X-Original-To: root@listes.minet.net
-Delivered-To: root@listes.minet.net
-Received: from mx1.minet.net (mx1.minet.net [192.168.102.25])
- by listes.minet.net (Postfix) with ESMTP id B8C1FA27FEF
- for <root@listes.minet.net>; Wed,  3 Jun 2015 09:05:46 +0000 (UTC)
-Received: from localhost (spam.minet.net [192.168.102.97])
- by mx1.minet.net (Postfix) with ESMTP id AD28B610F6D
- for <root@listes.minet.net>; Wed,  3 Jun 2015 11:05:46 +0200 (CEST)
-Content-Type: multipart/mixed; boundary="----------=_1433322346-12583-0"
-Content-Transfer-Encoding: 7bit
-MIME-Version: 1.0
-From: "Content-filter at spam.minet.net" <postmaster@minet.net>
-Date: Wed, 3 Jun 2015 09:05:46 +0000 (UTC)
-To: <root@listes.minet.net>
-Message-ID: <VASs-IZaXqmZao@spam.minet.net>
-Subject: Original subject
-X-BeenThere: root@listes.minet.net
-X-Mailman-Version: 2.1.15
-Errors-To: root-bounces@listes.minet.net
-Sender: "root" <root-bounces@listes.minet.net>
-
-This is a multi-part message in MIME format...
-
-------------=_1433322346-12583-0
-Content-Type: text/plain; charset="UTF-8"
-Content-Disposition: inline
-Content-Transfer-Encoding: 7bit
-
-No viruses were found.
-
-Content type: Unchecked
-Internal reference code for the message is 12583-16/Ss-IZaXqmZao
-
-According to a 'Received:' trace, the message apparently originated at:
-  [198.252.153.129], [127.0.0.1] localhost [127.0.0.1] Authenticated sender:
-  quentin.h
-
-Return-Path: <quentin.h@riseup.net>
-From: Quentin <quentin.h@riseup.net>
-Message-ID: <556EC361.6000308@riseup.net>
-Subject: =?UTF-8?B?UmU6IEZ3ZDogW1RtcGxhYl0gW0FQUEVMIEEgUEFSVElDSVBBVElPTl0=?=
-  =?UTF-8?B?IFdvb3QgZGV2aWNlcyAjMiAgOiB2ZW5leiBmYWlyZSBsZXVyIGhhY2tmw6p0ZSA=?=
-  =?UTF-8?B?YXV4IHBldGl0ZXMgbWFjaGluZXMgcHJvZ3JhbW1hYmxlcyBsZXMgNi03IGp1aW4=?=
-  =?UTF-8?B?IGF1IEphcmRpbiBkJ2FsaWNlL0JsYWNrbG9vcA==?=
-Not quarantined.
-
-The message WILL BE relayed to:
-<yann@minet.net>
-
-
-------------=_1433322346-12583-0
-Content-Type: text/rfc822-headers; name="header"
-Content-Disposition: inline; filename="header"
-Content-Transfer-Encoding: 7bit
-Content-Description: Message header section
-
-Return-Path: <quentin.h@riseup.net>
-Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=198.252.153.129; helo=mx1.riseup.net; envelope-from=quentin.h@riseup.net; receiver=yann@minet.net
-Authentication-Results: mx2.minet.net; dkim=pass
-	reason="1024-bit key; insecure key"
-	header.d=riseup.net header.i=@riseup.net header.b=RVs2xP3R;
-	dkim-adsp=pass; dkim-atps=neutral
-Received: from mx1.riseup.net (mx1.riseup.net [198.252.153.129])
-	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
-	(No client certificate requested)
-	by mx2.minet.net (Postfix) with ESMTPS id 6D9E0A27FCA
-	for <yann@minet.net>; Wed,  3 Jun 2015 11:05:44 +0200 (CEST)
-Received: from plantcutter.riseup.net (plantcutter-pn.riseup.net [10.0.1.121])
-	(using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits))
-	(Client CN "*.riseup.net", Issuer "COMODO RSA Domain Validation Secure Server CA" (verified OK))
-	by mx1.riseup.net (Postfix) with ESMTPS id 5CC6541A75;
-	Wed,  3 Jun 2015 09:05:42 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=riseup.net; s=squak;
-	t=1433322342; bh=Lz3x5ntYzOhQOpyviKamr5VE0TinfpBCnsivvJzojYk=;
-	h=Date:From:To:Subject:References:In-Reply-To:From;
-	b=RVs2xP3RV4zWi3Jbs6p85cO8s44yLq1LXxwe6NQvgZjsHQdWaMY16jQzSbCRRdCRY
-	 IesTpg1mncqIinhXxL9grw2+0MgO+E0HM0ZPZzXM/xNTGZpALI1X6tDM0rxB+GEnHa
-	 NfGRbWnesWFLyba7am02KbTKeKq7svIBBmSbaFsE=
-Received: from [127.0.0.1] (localhost [127.0.0.1])
-	(Authenticated sender: quentin.h)
-	with ESMTPSA id 6DBAC20827
-Message-ID: <556EC361.6000308@riseup.net>
-Date: Wed, 03 Jun 2015 11:05:37 +0200
-From: Quentin <quentin.h@riseup.net>
-User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.7.0
-MIME-Version: 1.0
-To: Yann Sionneau <yann@minet.net>, =?UTF-8?B?U3TDqXBoYW5pZSBPdWlsbG9u?=
- <stephanie@ouillon.fr>, =?UTF-8?B?TmF0YWxpYSBDYWxkZXJvbiBCZWx0csOhbg==?=
- <natalia@calderon.be>
-Subject: =?UTF-8?B?UmU6IEZ3ZDogW1RtcGxhYl0gW0FQUEVMIEEgUEFSVElDSVBBVElPTl0=?=
- =?UTF-8?B?IFdvb3QgZGV2aWNlcyAjMiAgOiB2ZW5leiBmYWlyZSBsZXVyIGhhY2tmw6p0ZSA=?=
- =?UTF-8?B?YXV4IHBldGl0ZXMgbWFjaGluZXMgcHJvZ3JhbW1hYmxlcyBsZXMgNi03IGp1aW4=?=
- =?UTF-8?B?IGF1IEphcmRpbiBkJ2FsaWNlL0JsYWNrbG9vcA==?=
-References: <556E332B.8020808@wootdevices.io> <556E3512.3050404@minet.net>
-In-Reply-To: <556E3512.3050404@minet.net>
-OpenPGP: id=AA5EE4B4EA20E2BEAA70B98E49CBF006922B990A;
-	url=pgp.mit.edu
-Content-Type: multipart/encrypted;
- protocol="application/pgp-encrypted";
- boundary="M0xVhKIvXqi85dG57o5RfCUAoFwhAw1Nh"
-X-Virus-Scanned: clamav-milter 0.98.7 at mx1
-X-Virus-Status: Clean
-
-------------=_1433322346-12583-0
-Content-Type: text/plain; charset="iso-8859-1"
-MIME-Version: 1.0
-Content-Transfer-Encoding: quoted-printable
-Content-Disposition: inline
-
-_______________________________________________
-root mailing list
-root@listes.minet.net
-https://listes.minet.net/cgi-bin/mailman/listinfo/root
-------------=_1433322346-12583-0--