You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2020/10/07 04:52:31 UTC
[james-project] branch master updated (7b951c1 -> a7a6546)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.
from 7b951c1 JAMES-3401 Handle Deserialize for FilterCondition when passing unsupported filter option on EmailQuery/MailboxQuery
new 08e3d1d JAMES-3404 Implicitly handle client id resolution with JSON substitution
new d3234f5 JAMES-3404 Demonstrate Email/query handle client id resolution for mailbox filters
new a7a6546 JAMES-3382 Email/query limit & position integration test fix
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../contract/EmailQueryMethodContract.scala | 230 +++++++++++++++++++--
.../contract/MailboxSetMethodContract.scala | 10 +-
.../apache/james/jmap/json/MailboxSerializer.scala | 3 +-
.../james/jmap/json/VacationSerializer.scala | 14 +-
.../org/apache/james/jmap/mail/MailboxGet.scala | 28 ++-
.../org/apache/james/jmap/mail/MailboxSet.scala | 30 +--
.../apache/james/jmap/mail/VacationResponse.scala | 1 +
.../james/jmap/method/MailboxGetMethod.scala | 15 +-
.../james/jmap/method/MailboxSetMethod.scala | 46 +++--
.../jmap/method/VacationResponseGetMethod.scala | 13 +-
.../james/jmap/routes/ProcessingContext.scala | 45 ++--
.../jmap/json/MailboxGetSerializationTest.scala | 18 +-
.../VacationResponseGetSerializationTest.scala | 3 -
13 files changed, 326 insertions(+), 130 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 02/03: JAMES-3404 Demonstrate Email/query handle
client id resolution for mailbox filters
Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit d3234f5d26b272951c8f4ed74a2653d25fb4b422
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 6 11:37:35 2020 +0700
JAMES-3404 Demonstrate Email/query handle client id resolution for mailbox filters
---
.../contract/EmailQueryMethodContract.scala | 194 +++++++++++++++++++++
1 file changed, 194 insertions(+)
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index f781e5d..ea20688 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -205,6 +205,200 @@ trait EmailQueryMethodContract {
}
@Test
+ def emailQueryShouldAcceptMailboxCreationIdForInMailboxOtherThan(server: GuiceJamesServer): Unit = {
+ val request =
+ s"""{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "create": {
+ | "C42": {
+ | "name": "myMailbox"
+ | }
+ | }
+ | },
+ | "c1"
+ | ], [
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter" : {
+ | "inMailboxOtherThan": ["#C42"]
+ | }
+ | },
+ | "c2"
+ | ]]
+ |}""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+ .getMailboxId("#private", BOB.asString(), "myMailbox")
+ .serialize
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "created": {
+ | "C42": {
+ | "id": "${mailboxId}",
+ | "sortOrder": 1000,
+ | "totalEmails": 0,
+ | "unreadEmails": 0,
+ | "totalThreads": 0,
+ | "unreadThreads": 0,
+ | "myRights": {
+ | "mayReadItems": true,
+ | "mayAddItems": true,
+ | "mayRemoveItems": true,
+ | "maySetSeen": true,
+ | "maySetKeywords": true,
+ | "mayCreateChild": true,
+ | "mayRename": true,
+ | "mayDelete": true,
+ | "maySubmit": true
+ | },
+ | "isSubscribed": true
+ | }
+ | }
+ | },
+ | "c1"
+ | ],
+ | [
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "00000000",
+ | "canCalculateChanges": false,
+ | "ids": [
+ |
+ | ],
+ | "position": 0,
+ | "limit": 256
+ | },
+ | "c2"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def emailQueryShouldAcceptMailboxCreationIdForInMailbox(server: GuiceJamesServer): Unit = {
+ val request =
+ s"""{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "create": {
+ | "C42": {
+ | "name": "myMailbox"
+ | }
+ | }
+ | },
+ | "c1"
+ | ], [
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter" : {
+ | "inMailbox": "#C42"
+ | }
+ | },
+ | "c2"
+ | ]]
+ |}""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+ .getMailboxId("#private", BOB.asString(), "myMailbox")
+ .serialize
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "created": {
+ | "C42": {
+ | "id": "${mailboxId}",
+ | "sortOrder": 1000,
+ | "totalEmails": 0,
+ | "unreadEmails": 0,
+ | "totalThreads": 0,
+ | "unreadThreads": 0,
+ | "myRights": {
+ | "mayReadItems": true,
+ | "mayAddItems": true,
+ | "mayRemoveItems": true,
+ | "maySetSeen": true,
+ | "maySetKeywords": true,
+ | "mayCreateChild": true,
+ | "mayRename": true,
+ | "mayDelete": true,
+ | "maySubmit": true
+ | },
+ | "isSubscribed": true
+ | }
+ | }
+ | },
+ | "c1"
+ | ],
+ | [
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "00000000",
+ | "canCalculateChanges": false,
+ | "ids": [
+ |
+ | ],
+ | "position": 0,
+ | "limit": 256
+ | },
+ | "c2"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
def emailInSharedMailboxesShouldNotBeDisplayedWhenNoExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 03/03: JAMES-3382 Email/query limit & position
integration test fix
Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit a7a65461e41ca3d1feffe442916eda3a023e367d
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Tue Oct 6 16:35:10 2020 +0700
JAMES-3382 Email/query limit & position integration test fix
---
.../contract/EmailQueryMethodContract.scala | 36 ++++++++++++++--------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index ea20688..33c53b3 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -3176,33 +3176,43 @@ trait EmailQueryMethodContract {
@Test
def shouldLimitResultByLimitAndPosition(server: GuiceJamesServer): Unit = {
+ val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
+
val message: Message = Message.Builder
.of
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
.build
- server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val otherMailboxPath = MailboxPath.forUser(BOB, "other")
- val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
- sendMessageToBobInbox(server, message, Date.from(requestDate.toInstant))
+ mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
- server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
- val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
- .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+ val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+ mailboxProbe.createMailbox(otherMailboxPath)
+ server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+ .withInternalDate(Date.from(ZonedDateTime.now().minusDays(4).toInstant))
+ .build(message))
.getMessageId
- val messageId3: MessageId = server.getProbe(classOf[MailboxProbeImpl])
- .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+ val messageId2: MessageId = mailboxProbe
+ .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+ .withInternalDate(Date.from(ZonedDateTime.now().minusDays(3).toInstant))
+ .build(message))
.getMessageId
- server.getProbe(classOf[MailboxProbeImpl])
- .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+ val messageId3: MessageId = mailboxProbe
+ .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+ .withInternalDate(Date.from(ZonedDateTime.now().minusDays(2).toInstant))
+ .build(message))
.getMessageId
- server.getProbe(classOf[MailboxProbeImpl])
- .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+ mailboxProbe
+ .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+ .withInternalDate(Date.from(ZonedDateTime.now().minusDays(1).toInstant))
+ .build(message))
.getMessageId
+ sendMessageToBobInbox(server, message, Date.from(Date.from(ZonedDateTime.now().toInstant).toInstant))
+
val request =
s"""{
| "using": [
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 01/03: JAMES-3404 Implicitly handle client id
resolution with JSON substitution
Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 08e3d1dec0f1ae74b3621d3aea9b174db1661596
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 6 11:28:34 2020 +0700
JAMES-3404 Implicitly handle client id resolution with JSON substitution
This empowers implicit handling, making it easier to implement client Id resolution
---
.../contract/MailboxSetMethodContract.scala | 10 ++---
.../apache/james/jmap/json/MailboxSerializer.scala | 3 +-
.../james/jmap/json/VacationSerializer.scala | 14 ++++---
.../org/apache/james/jmap/mail/MailboxGet.scala | 28 ++++++++++++-
.../org/apache/james/jmap/mail/MailboxSet.scala | 30 +++++---------
.../apache/james/jmap/mail/VacationResponse.scala | 1 +
.../james/jmap/method/MailboxGetMethod.scala | 15 ++++---
.../james/jmap/method/MailboxSetMethod.scala | 46 ++++++++++++----------
.../jmap/method/VacationResponseGetMethod.scala | 13 +++---
.../james/jmap/routes/ProcessingContext.scala | 45 +++++++++------------
.../jmap/json/MailboxGetSerializationTest.scala | 18 +--------
.../VacationResponseGetSerializationTest.scala | 3 --
12 files changed, 109 insertions(+), 117 deletions(-)
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index 413aadb..c81aef5 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -2807,7 +2807,7 @@ trait MailboxSetMethodContract {
| "notDestroyed": {
| "#C42": {
| "type": "invalidArguments",
- | "description": "#C42 is not a mailboxId: ClientId(#C42) was not used in previously defined creationIds"
+ | "description": "#C42 is not a mailboxId: #C42 was not used in previously defined creationIds"
| }
| }
| }, "c2"],
@@ -2881,7 +2881,7 @@ trait MailboxSetMethodContract {
| "notDestroyed": {
| "#C42": {
| "type": "invalidArguments",
- | "description": "#C42 is not a mailboxId: ClientId(#C42) was not used in previously defined creationIds"
+ | "description": "#C42 is not a mailboxId: #C42 was not used in previously defined creationIds"
| }
| }
| }, "c2"]
@@ -2919,7 +2919,7 @@ trait MailboxSetMethodContract {
.body
.asString
- val message = "# is not a mailboxId: Left predicate of ((!(0 < 1) && !(0 > 255)) && \\\"\\\".matches(\\\"^[#a-zA-Z0-9-_]*$\\\")) failed: Predicate taking size() = 0 failed: Left predicate of (!(0 < 1) && !(0 > 255)) failed: Predicate (0 < 1) did not fail."
+ val message = "# is not a mailboxId: # was not used in previously defined creationIds"
assertThatJson(response).isEqualTo(
s"""{
| "sessionState": "75128aab4b1b",
@@ -6728,7 +6728,7 @@ trait MailboxSetMethodContract {
| "notUpdated": {
| "${mailboxId.serialize}": {
| "type": "invalidArguments",
- | "description": "ClientId(#C42) was not used in previously defined creationIds",
+ | "description": "#C42 was not used in previously defined creationIds",
| "properties": ["parentId"]
| }
| }
@@ -7357,7 +7357,7 @@ trait MailboxSetMethodContract {
| "notUpdated": {
| "#invalid": {
| "type": "invalidArguments",
- | "description": "ClientId(#invalid) was not used in previously defined creationIds"
+ | "description": "#invalid was not used in previously defined creationIds"
| }
| }
| },
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
index d8e4467..edb6d11 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
@@ -23,7 +23,8 @@ import eu.timepit.refined._
import eu.timepit.refined.collection.NonEmpty
import javax.inject.Inject
import org.apache.james.core.{Domain, Username}
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxGet.{UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, RemoveEmailsOnDestroy, [...]
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
index 70f61b6..38c830c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
@@ -21,9 +21,8 @@ package org.apache.james.jmap.json
import java.time.format.DateTimeFormatter
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
import org.apache.james.jmap.mail.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
-import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, NotFound, Subject, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound}
+import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, Subject, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound}
import org.apache.james.jmap.model._
import org.apache.james.jmap.vacation.{VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
import play.api.libs.json._
@@ -38,9 +37,6 @@ object VacationSerializer {
}
private implicit val vacationResponseSetRequestReads: Reads[VacationResponseSetRequest] = Json.reads[VacationResponseSetRequest]
- private implicit def notFoundWrites(implicit mailboxIdWrites: Writes[UnparsedMailboxId]): Writes[NotFound] =
- notFound => JsArray(notFound.value.toList.map(mailboxIdWrites.writes))
-
private implicit val vacationResponseSetUpdateResponseWrites: Writes[VacationResponseUpdateResponse] = Json.valueWrites[VacationResponseUpdateResponse]
private implicit val vacationResponseSetErrorWrites: Writes[VacationResponseSetError] = Json.writes[VacationResponseSetError]
@@ -51,6 +47,11 @@ object VacationSerializer {
utcDate => JsString(utcDate.asUTC.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssX")))
private implicit val vacationResponseIdWrites: Writes[VacationResponseId] = _ => JsString(VACATION_RESPONSE_ID.value)
+ private implicit val vacationResponseIdReads: Reads[VacationResponseId] = {
+ case JsString("singleton") => JsSuccess(VacationResponseId())
+ case JsString(_) => JsError("Only singleton is supported as a VacationResponseId")
+ case _ => JsError("Expecting JsString(singleton) to represent a VacationResponseId")
+ }
private implicit val isEnabledWrites: Writes[IsEnabled] = Json.valueWrites[IsEnabled]
private implicit val fromDateWrites: Writes[FromDate] = Json.valueWrites[FromDate]
private implicit val toDateWrites: Writes[ToDate] = Json.valueWrites[ToDate]
@@ -61,7 +62,8 @@ object VacationSerializer {
implicit def vacationResponseWrites(properties: Properties): Writes[VacationResponse] = Json.writes[VacationResponse]
.transform(properties.filter(_))
- private implicit val vacationResponseIdReads: Reads[VacationResponseIds] = Json.valueReads[VacationResponseIds]
+ private implicit val vacationResponseIdsReads: Reads[VacationResponseIds] = Json.valueReads[VacationResponseIds]
+
private implicit val vacationResponseGetRequest: Reads[VacationResponseGetRequest] = Json.reads[VacationResponseGetRequest]
private implicit def vacationResponseNotFoundWrites(implicit idWrites: Writes[UnparsedVacationResponseId]): Writes[VacationResponseNotFound] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
index b230a77..bd7b8ba 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -19,10 +19,36 @@
package org.apache.james.jmap.mail
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import eu.timepit.refined
+import eu.timepit.refined.api.Refined
+import eu.timepit.refined.collection.NonEmpty
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
import org.apache.james.jmap.method.WithAccountId
import org.apache.james.jmap.model.State.State
import org.apache.james.jmap.model.{AccountId, Properties}
+import org.apache.james.mailbox.model.MailboxId
+
+import scala.util.{Failure, Try}
+
+object MailboxGet {
+ type UnparsedMailboxIdConstraint = NonEmpty
+ type UnparsedMailboxId = String Refined UnparsedMailboxIdConstraint
+
+ def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = refined.refineV[UnparsedMailboxIdConstraint](mailboxId.serialize()) match {
+ case Left(e) => throw new IllegalArgumentException(e)
+ case scala.Right(value) => value
+ }
+
+ def parse(mailboxIdFactory: MailboxId.Factory)(unparsed: UnparsedMailboxId): Try[MailboxId] =
+ parseString(mailboxIdFactory)(unparsed.value)
+
+ def parseString(mailboxIdFactory: MailboxId.Factory)(unparsed: String): Try[MailboxId] =
+ unparsed match {
+ case a if a.startsWith("#") =>
+ Failure(new IllegalArgumentException(s"$unparsed was not used in previously defined creationIds"))
+ case _ => Try(mailboxIdFactory.fromString(unparsed))
+ }
+}
case class Ids(value: List[UnparsedMailboxId])
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
index cbd034e..20801bd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
@@ -19,7 +19,6 @@
package org.apache.james.jmap.mail
-import eu.timepit.refined
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection.NonEmpty
@@ -27,15 +26,15 @@ import eu.timepit.refined.refineV
import eu.timepit.refined.types.string.NonEmptyString
import org.apache.james.core.Username
import org.apache.james.jmap.json.MailboxSerializer
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
import org.apache.james.jmap.mail.MailboxName.MailboxName
import org.apache.james.jmap.mail.MailboxPatchObject.MailboxPatchObjectKey
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
import org.apache.james.jmap.method.{MailboxCreationParseException, WithAccountId}
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType}
import org.apache.james.jmap.model.State.State
import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier, Properties, SetError}
-import org.apache.james.jmap.routes.ProcessingContext
import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
import org.apache.james.mailbox.{MailboxSession, Role}
import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsString, JsSuccess, JsValue}
@@ -48,14 +47,7 @@ case class MailboxSetRequest(accountId: AccountId,
onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy]) extends WithAccountId
object MailboxSetRequest {
- type UnparsedMailboxIdConstraint = NonEmpty
type MailboxCreationId = String Refined NonEmpty
- type UnparsedMailboxId = String Refined UnparsedMailboxIdConstraint
-
- def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = refined.refineV[UnparsedMailboxIdConstraint](mailboxId.serialize()) match {
- case Left(e) => throw new IllegalArgumentException(e)
- case scala.Right(value) => value
- }
}
case class RemoveEmailsOnDestroy(value: Boolean) extends AnyVal
@@ -114,12 +106,11 @@ object MailboxPatchObject {
}
case class MailboxPatchObject(value: Map[String, JsValue]) {
- def validate(processingContext: ProcessingContext,
- mailboxIdFactory: MailboxId.Factory,
+ def validate(mailboxIdFactory: MailboxId.Factory,
serializer: MailboxSerializer,
capabilities: Set[CapabilityIdentifier],
mailboxSession: MailboxSession): Either[PatchUpdateValidationException, ValidatedMailboxPatchObject] = {
- val asUpdatedIterable = updates(serializer, capabilities, processingContext, mailboxIdFactory, mailboxSession)
+ val asUpdatedIterable = updates(serializer, capabilities, mailboxIdFactory, mailboxSession)
val maybeParseException: Option[PatchUpdateValidationException] = asUpdatedIterable
.flatMap(x => x match {
@@ -174,12 +165,11 @@ case class MailboxPatchObject(value: Map[String, JsValue]) {
def updates(serializer: MailboxSerializer,
capabilities: Set[CapabilityIdentifier],
- processingContext: ProcessingContext,
mailboxIdFactory: MailboxId.Factory,
mailboxSession: MailboxSession): Iterable[Either[PatchUpdateValidationException, Update]] = value.map({
case (property, newValue) => property match {
case "name" => NameUpdate.parse(newValue, mailboxSession)
- case "parentId" => ParentIdUpdate.parse(newValue, processingContext, mailboxIdFactory)
+ case "parentId" => ParentIdUpdate.parse(newValue, mailboxIdFactory)
case "sharedWith" => SharedWithResetUpdate.parse(serializer, capabilities)(newValue)
case "role" => Left(ServerSetPropertyException(MailboxPatchObject.roleProperty))
case "sortOrder" => Left(ServerSetPropertyException(MailboxPatchObject.sortOrderProperty))
@@ -331,15 +321,13 @@ object SharedWithPartialUpdate {
}
object ParentIdUpdate {
- def parse(newValue: JsValue, processingContext: ProcessingContext, mailboxIdFactory: MailboxId.Factory):
+ def parse(newValue: JsValue, mailboxIdFactory: MailboxId.Factory):
Either[PatchUpdateValidationException, Update] =
(newValue match {
case JsString(id) =>
- for {
- unparsedMailboxId <- refineV[UnparsedMailboxIdConstraint](id)
- mailboxId <- processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).left.map(_.getMessage)
- } yield changeParentTo(mailboxId)
-
+ MailboxGet.parseString(mailboxIdFactory)(id)
+ .fold(e => Left(e.getMessage),
+ mailboxId => scala.Right(changeParentTo(mailboxId)))
case JsNull => scala.Right(removeParent())
case _ => Left("Expecting a JSON string or null as an argument")
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
index db02ec3..d25c603 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
@@ -38,6 +38,7 @@ case class HtmlBody(value: String)
object VacationResponse {
val VACATION_RESPONSE_ID: Id = "singleton"
+ val UNPARSED_SINGLETON: UnparsedVacationResponseId = "singleton"
type UnparsedVacationResponseId = String Refined NonEmpty
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 15c01c9..154da66 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -23,14 +23,13 @@ import eu.timepit.refined.auto._
import javax.inject.Inject
import org.apache.james.jmap.http.SessionSupplier
import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
import org.apache.james.jmap.mail._
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
import org.apache.james.jmap.model.State.INSTANCE
import org.apache.james.jmap.model.{AccountId, Capabilities, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions}
-import org.apache.james.jmap.routes.ProcessingContext
import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
import org.apache.james.mailbox.exception.MailboxNotFoundException
import org.apache.james.mailbox.model.search.MailboxQuery
@@ -42,6 +41,7 @@ import reactor.core.scala.publisher.{SFlux, SMono}
import reactor.core.scheduler.Schedulers
import scala.jdk.CollectionConverters._
+import scala.util.Try
class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
mailboxManager: MailboxManager,
@@ -59,7 +59,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
def empty(): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set.empty))
def found(mailbox: Mailbox): MailboxGetResults = MailboxGetResults(Set(mailbox), NotFound(Set.empty))
def notFound(mailboxId: UnparsedMailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(mailboxId)))
- def notFound(mailboxId: MailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(MailboxSetRequest.asUnparsed(mailboxId))))
+ def notFound(mailboxId: MailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(MailboxGet.asUnparsed(mailboxId))))
}
case class MailboxGetResults(mailboxes: Set[Mailbox], notFound: NotFound) {
@@ -75,7 +75,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxGetRequest): SMono[InvocationWithContext] = {
val requestedProperties: Properties = request.properties.getOrElse(Mailbox.allProperties)
(requestedProperties -- Mailbox.allProperties match {
- case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, request, invocation.processingContext, mailboxSession)
+ case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, request, mailboxSession)
.reduce(MailboxGetResults.empty(), MailboxGetResults.merge)
.map(mailboxes => mailboxes.asResponse(request.accountId))
.map(mailboxGetResponse => Invocation(
@@ -101,16 +101,15 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
private def getMailboxes(capabilities: Set[CapabilityIdentifier],
mailboxGetRequest: MailboxGetRequest,
- processingContext: ProcessingContext,
mailboxSession: MailboxSession): SFlux[MailboxGetResults] =
mailboxGetRequest.ids match {
case None => getAllMailboxes(capabilities, mailboxSession)
.map(MailboxGetResults.found)
case Some(ids) => SFlux.fromIterable(ids.value)
- .flatMap(id => processingContext.resolveMailboxId(id, mailboxIdFactory)
- .fold(e => SMono.just(MailboxGetResults.notFound(id)),
- mailboxId => getMailboxResultById(capabilities, mailboxId, mailboxSession)))
+ .flatMap(id => Try(mailboxIdFactory.fromString(id.value))
+ .fold(e => SMono.just(MailboxGetResults.notFound(id)),
+ mailboxId => getMailboxResultById(capabilities, mailboxId, mailboxSession)))
}
private def getMailboxResultById(capabilities: Set[CapabilityIdentifier],
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index 5c9a50a..6a40581 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -23,8 +23,9 @@ import eu.timepit.refined.auto._
import javax.inject.Inject
import org.apache.james.jmap.http.SessionSupplier
import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId}
-import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
+import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxGet, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMai [...]
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
@@ -42,6 +43,7 @@ import reactor.core.scala.publisher.{SFlux, SMono}
import reactor.core.scheduler.Schedulers
import scala.jdk.CollectionConverters._
+import scala.util.Try
case class MailboxHasMailException(mailboxId: MailboxId) extends Exception
case class SystemMailboxChangeException(mailboxId: MailboxId) extends Exception
@@ -155,7 +157,7 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for {
creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, invocation.processingContext)
- deletionResults <- deleteMailboxes(mailboxSession, request, invocation.processingContext)
+ deletionResults <- deleteMailboxes(mailboxSession, request)
updateResults <- updateMailboxes(mailboxSession, request, invocation.processingContext, capabilities)
} yield InvocationWithContext(createResponse(capabilities, invocation.invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
@@ -168,9 +170,10 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq()))
.flatMap({
case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
- processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).fold(
+ MailboxGet.parse(mailboxIdFactory)(unparsedMailboxId)
+ .fold(
e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)),
- mailboxId => updateMailbox(mailboxSession, processingContext, mailboxId, unparsedMailboxId, patch, capabilities))
+ mailboxId => updateMailbox(mailboxSession, mailboxId, unparsedMailboxId, patch, capabilities))
.onErrorResume(e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)))
})
.collectSeq()
@@ -178,12 +181,11 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
}
private def updateMailbox(mailboxSession: MailboxSession,
- processingContext: ProcessingContext,
mailboxId: MailboxId,
unparsedMailboxId: UnparsedMailboxId,
patch: MailboxPatchObject,
capabilities: Set[CapabilityIdentifier]): SMono[UpdateResult] = {
- patch.validate(processingContext, mailboxIdFactory, serializer, capabilities, mailboxSession)
+ patch.validate(mailboxIdFactory, serializer, capabilities, mailboxSession)
.fold(e => SMono.raiseError(e), validatedPatch =>
updateMailboxRights(mailboxId, validatedPatch, mailboxSession)
.`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession))
@@ -299,24 +301,24 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
}
- private def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest, processingContext: ProcessingContext): SMono[DeletionResults] = {
+ private def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest): SMono[DeletionResults] = {
SFlux.fromIterable(mailboxSetRequest.destroy.getOrElse(Seq()))
- .flatMap(id => delete(mailboxSession, processingContext, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
+ .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
.onErrorRecover(e => DeletionFailure(id, e)))
.collectSeq()
.map(DeletionResults)
}
- private def delete(mailboxSession: MailboxSession, processingContext: ProcessingContext, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[DeletionResult] = {
- processingContext.resolveMailboxId(id, mailboxIdFactory) match {
- case Right(mailboxId) => SMono.fromCallable(() => delete(mailboxSession, mailboxId, onDestroy))
- .subscribeOn(Schedulers.elastic())
- .`then`(SMono.just[DeletionResult](DeletionSuccess(mailboxId)))
- case Left(e) => SMono.raiseError(e)
- }
+ private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[DeletionResult] = {
+ MailboxGet.parse(mailboxIdFactory)(id)
+ .fold(e => SMono.raiseError(e),
+ id => SMono.fromCallable(() => doDelete(mailboxSession, id, onDestroy))
+ .subscribeOn(Schedulers.elastic())
+ .`then`(SMono.just[DeletionResult](DeletionSuccess(id))))
+
}
- private def delete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
+ private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
val mailbox = mailboxManager.getMailbox(id, mailboxSession)
if (isASystemMailbox(mailbox)) {
@@ -363,7 +365,7 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
jsObject: JsObject,
processingContext: ProcessingContext): (CreationResult, ProcessingContext) = {
parseCreate(jsObject)
- .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest, processingContext)
+ .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest)
.flatMap(path => createMailbox(mailboxSession = mailboxSession,
path = path,
mailboxCreationRequest = mailboxCreationRequest)))
@@ -441,14 +443,16 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
}
private def resolvePath(mailboxSession: MailboxSession,
- mailboxCreationRequest: MailboxCreationRequest,
- processingContext: ProcessingContext): Either[Exception, MailboxPath] = {
+ mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxPath] = {
if (mailboxCreationRequest.name.value.contains(mailboxSession.getPathDelimiter)) {
return Left(new MailboxNameException(s"The mailbox '${mailboxCreationRequest.name.value}' contains an illegal character: '${mailboxSession.getPathDelimiter}'"))
}
mailboxCreationRequest.parentId
.map(maybeParentId => for {
- parentId <- processingContext.resolveMailboxId(maybeParentId, mailboxIdFactory)
+ parentId <- Try(mailboxIdFactory.fromString(maybeParentId.value))
+ .toEither
+ .left
+ .map(e => new IllegalArgumentException(e.getMessage, e))
parentPath <- retrievePath(parentId, mailboxSession)
} yield {
parentPath.child(mailboxCreationRequest.name, mailboxSession.getPathDelimiter)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 2978afd..ebbc939 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -24,14 +24,13 @@ import javax.inject.Inject
import org.apache.james.jmap.api.vacation.{VacationRepository, AccountId => JavaAccountId}
import org.apache.james.jmap.http.SessionSupplier
import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
-import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
+import org.apache.james.jmap.mail.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY}
import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
import org.apache.james.jmap.model.State.INSTANCE
import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, MissingCapabilityException, Properties}
-import org.apache.james.jmap.routes.ProcessingContext
import org.apache.james.mailbox.MailboxSession
import org.apache.james.metrics.api.MetricFactory
import play.api.libs.json.{JsError, JsObject, JsSuccess}
@@ -67,7 +66,7 @@ class VacationResponseGetMethod @Inject()(vacationRepository: VacationRepository
{
val requestedProperties: Properties = request.properties.getOrElse(VacationResponse.allProperties)
(requestedProperties -- VacationResponse.allProperties match {
- case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(request, invocation.processingContext, mailboxSession)
+ case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(request, mailboxSession)
.reduce(VacationResponseGetResult.empty, VacationResponseGetResult.merge)
.map(vacationResult => vacationResult.asResponse(request.accountId))
.map(vacationResponseGetResponse => Invocation(
@@ -97,16 +96,16 @@ class VacationResponseGetMethod @Inject()(vacationRepository: VacationRepository
}
private def getVacationResponse(vacationResponseGetRequest: VacationResponseGetRequest,
- processingContext: ProcessingContext,
mailboxSession: MailboxSession): SFlux[VacationResponseGetResult] =
vacationResponseGetRequest.ids match {
case None => getVacationSingleton(mailboxSession)
.map(VacationResponseGetResult.found)
.flux()
case Some(ids) => SFlux.fromIterable(ids.value)
- .flatMap(id => processingContext.resolveVacationResponseId(id)
- .fold(_ => SMono.just(VacationResponseGetResult.notFound(id)),
- _ => getVacationSingleton(mailboxSession).map(VacationResponseGetResult.found)))
+ .flatMap(id => id match {
+ case UNPARSED_SINGLETON => getVacationSingleton(mailboxSession).map(VacationResponseGetResult.found)
+ case _ => SMono.just(VacationResponseGetResult.notFound(id))
+ })
}
private def getVacationSingleton(mailboxSession: MailboxSession): SMono[VacationResponse] = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
index 8506f2f..a814df4 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
@@ -23,13 +23,9 @@ import eu.timepit.refined.numeric.NonNegative
import eu.timepit.refined.refineV
import eu.timepit.refined.types.numeric.NonNegInt
import org.apache.james.jmap.json.BackReferenceDeserializer
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
-import org.apache.james.jmap.mail.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
-import org.apache.james.jmap.model.Id.Id
import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId}
-import org.apache.james.mailbox.model.MailboxId
-import play.api.libs.json.{JsArray, JsError, JsObject, JsResult, JsSuccess, JsValue, Reads}
+import play.api.libs.json.{JsArray, JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Reads}
import scala.util.Try
@@ -115,7 +111,7 @@ case class JsonPath(parts: List[JsonPathPart]) {
}
}
- private def readWildcard(jsValue: JsValue) = jsValue match {
+ private def readWildcard(jsValue: JsValue): JsResult[JsValue] = jsValue match {
case JsArray(arrayItems) =>
val evaluationResults: List[JsResult[JsValue]] = arrayItems.toList.map(evaluate)
@@ -146,8 +142,7 @@ case class InvalidResultReferenceException(message: String) extends IllegalArgum
case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], private val invocations: Map[MethodCallId, Invocation]) {
- def recordCreatedId(clientId: ClientId, serverId: ServerId): ProcessingContext = ProcessingContext(creationIds + (clientId -> serverId), invocations)
- private def retrieveServerId(clientId: ClientId): Option[ServerId] = creationIds.get(clientId)
+ def recordCreatedId(clientId: ClientId, serverId: ServerId): ProcessingContext = ProcessingContext(creationIds + (clientId -> serverId), invocations)
def recordInvocation(invocation: Invocation): ProcessingContext = ProcessingContext(creationIds, invocations + (invocation.methodCallId -> invocation))
@@ -163,6 +158,9 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
private def backReferenceResolver(): Reads[JsValue] = {
case JsArray(value) => resolveBackReferences(value)
case JsObject(underlying) => resolveBackReference(underlying)
+ case JsString(value) if value.startsWith("#") => resolveCreationId(value)
+ .fold(_ => JsSuccess(JsString(value)),
+ serverId => JsSuccess(JsString(serverId.value.value)))
case others: JsValue => JsSuccess(others)
}
@@ -174,7 +172,7 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
}
private def resolveBackReference(underlying: collection.Map[String, JsValue]): JsResult[JsObject] = {
- val resolutions = underlying.map(resolveBackReference)
+ val resolutions = underlying.map(resolveBackReference(_))
val firstError = resolutions.flatMap({
case Left(jsError) => Some(jsError)
@@ -196,7 +194,12 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
BackReferenceDeserializer.deserializeBackReference(entry._2) match {
case JsSuccess(backReference, _) => resolveBackReference(newEntry, backReference)
// If the JSON object is not a back-reference continue parsing (it could be a creationId)
- case JsError(_) => propagateBackReferenceResolution(entry)
+ case JsError(_) =>
+ backReferenceResolver().reads(entry._2)
+ .fold(e => Left(JsError(e)),
+ json => resolveCreationId(entry._1)
+ .fold(_ => Right((entry._1, json)),
+ serverId => Right((serverId.value.value, json))))
}
} else {
propagateBackReferenceResolution(entry)
@@ -225,17 +228,9 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
.map(backReference.resolve)
.getOrElse(JsError("Back reference could not be resolved"))
- def resolveMailboxId(unparsedMailboxId: UnparsedMailboxId, mailboxIdFactory: MailboxId.Factory): Either[IllegalArgumentException, MailboxId] =
- Id.validate(unparsedMailboxId.value)
- .flatMap(id => resolveServerId(ClientId(id)))
- .flatMap(serverId => parseMailboxId(mailboxIdFactory, serverId))
-
- private def parseMailboxId(mailboxIdFactory: MailboxId.Factory, serverId: ServerId) =
- try {
- Right(mailboxIdFactory.fromString(serverId.value.value))
- } catch {
- case e: IllegalArgumentException => Left(e)
- }
+ private def resolveCreationId(creationId: String): Either[IllegalArgumentException, ServerId] =
+ Id.validate(creationId)
+ .flatMap(id => resolveServerId(ClientId(id)))
private def resolveServerId(id: ClientId): Either[IllegalArgumentException, ServerId] =
id.retrieveOriginalClientId
@@ -244,10 +239,6 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
.getOrElse(Left[IllegalArgumentException, ServerId](new IllegalArgumentException(s"$id was not used in previously defined creationIds")))))
.getOrElse(Right(ServerId(id.value)))
- def resolveVacationResponseId(unparsedVacationId: UnparsedVacationResponseId): Either[IllegalArgumentException, Id] =
- if (unparsedVacationId.equals(VACATION_RESPONSE_ID)) {
- Right(VACATION_RESPONSE_ID)
- } else {
- Left(new IllegalArgumentException(s"$unparsedVacationId is not a valid VacationResponse ID"))
- }
+ private def retrieveServerId(clientId: ClientId): Option[ServerId] = creationIds.get(clientId)
+
}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
index de24595..c2841fc 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
@@ -24,7 +24,7 @@ import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
import org.apache.james.jmap.json.Fixture._
import org.apache.james.jmap.json.MailboxGetSerializationTest._
import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
import org.apache.james.jmap.mail._
import org.apache.james.jmap.model.{AccountId, Properties}
import org.apache.james.mailbox.model.{MailboxId, TestId}
@@ -47,22 +47,6 @@ object MailboxGetSerializationTest {
class MailboxGetSerializationTest extends AnyWordSpec with Matchers {
"Deserialize MailboxGetRequest" should {
- "succeed on invalid mailboxId" in {
- // as they are unparsed
- val expectedRequestObject = MailboxGetRequest(
- accountId = ACCOUNT_ID,
- ids = Some(Ids(List("ab#?"))),
- properties = None)
-
- SERIALIZER.deserializeMailboxGetRequest(
- """
- |{
- | "accountId": "aHR0cHM6Ly93d3cuYmFzZTY0ZW5jb2RlLm9yZy8",
- | "ids": ["ab#?"]
- |}
- |""".stripMargin) should equal(JsSuccess(expectedRequestObject))
- }
-
"succeed when properties are missing" in {
val expectedRequestObject = MailboxGetRequest(
accountId = ACCOUNT_ID,
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
index e3028aa..35a0e4e 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
@@ -27,14 +27,11 @@ import org.apache.james.jmap.json.VacationResponseSerializationTest.VACATION_RES
import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
import org.apache.james.jmap.model.{AccountId, Properties}
-import org.apache.james.mailbox.model.{MailboxId, TestId}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import play.api.libs.json.{JsSuccess, Json}
object VacationResponseGetSerializationTest {
- private val FACTORY: MailboxId.Factory = new TestId.Factory
-
private val ACCOUNT_ID: AccountId = AccountId(id)
private val SINGLETON_ID: UnparsedVacationResponseId = "singleton"
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org