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/09/25 01:51:45 UTC
[james-project] branch master updated (2575ea3 -> 409f94c)
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 2575ea3 [Fix] The latest stable image of James still uses swift by default with docker-compose
new 546550f JAMES-3379 Email/get specific parsed headers: asGroupedAddresses
new d5717e4 JAMES-3379 Email/get specific parsed headers: asMessageIds
new 00441eb JAMES-3379 Email/get specific parsed headers: asDate
new 7ce20ab JAMES-3379 Email/get specific parsed headers: asURLs
new 0e0b643 JAMES-3379 Remove unused Option
new 25fa489 JAMES-3305 James do not start if eventBus workQueues don't rely on dead-letter
new c342b13 JAMES-3305 MailQueue creation should not fail when no dead-letter set up
new c13a78a JAMES-3305 Extract dispatchShouldWorkAfterNetworkIssuesForOldRegistration in its own class
new 871d5f2 JAMES-3375 MailboxManager::search should enable finner grained control over the search mailboxes
new 03a8293 JAMES-3359 Reject Mailbox/set parentId update to self
new 662604a JAMES-3359 Remove needless brackets
new 9a85562 JAMES-3386 do not allow blank mailbox path
new 409f94c JAMES-3386 add test to ensure blank mailbox paths are not allowed in jmap draft
The 13 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:
.../rabbitmq/ReactorRabbitMQChannelPool.java | 28 +
.../apache/james/mailbox/model/MailboxPath.java | 13 +-
.../mailbox/model/MultimailboxesSearchQuery.java | 74 +-
.../apache/james/mailbox/MailboxManagerTest.java | 68 ++
.../james/mailbox/model/MailboxPathTest.java | 54 ++
.../model/MultimailboxesSearchQueryTest.java | 13 +-
.../james/mailbox/events/GroupRegistration.java | 17 +-
.../mailbox/events/GroupRegistrationHandler.java | 7 +-
.../james/mailbox/events/RabbitMQEventBus.java | 9 +-
.../james/mailbox/events/NetworkErrorTest.java | 87 +++
...RabbitMQEventBusDeadLetterQueueUpgradeTest.java | 87 +++
.../james/mailbox/events/RabbitMQEventBusTest.java | 23 +-
.../james/mailbox/store/StoreMailboxManager.java | 24 +-
.../rabbitmq/host/RabbitMQEventBusHostSystem.java | 3 +-
.../integration/SetMailboxesMethodTest.java | 61 ++
.../rfc8621/contract/EmailGetMethodContract.scala | 699 +++++++++++++++++-
.../contract/EmailQueryMethodContract.scala | 398 ++++++++++-
.../contract/MailboxSetMethodContract.scala | 781 ++++++++++++++-------
.../james/jmap/json/EmailGetSerializer.scala | 16 +-
.../scala/org/apache/james/jmap/mail/Email.scala | 71 +-
.../apache/james/jmap/mail/EmailAddressGroup.scala | 7 +-
.../org/apache/james/jmap/mail/EmailGet.scala | 13 +-
.../org/apache/james/jmap/mail/EmailHeader.scala | 83 ++-
.../scala/org/apache/james/jmap/mail/Mailbox.scala | 5 +-
.../james/jmap/method/EmailQueryMethod.scala | 14 +-
.../james/jmap/method/MailboxSetMethod.scala | 5 +
.../apache/james/jmap/model/MailboxFactory.scala | 5 +-
.../james/jmap/utils/search/MailboxFilter.scala | 13 +-
.../rabbitmq/RabbitMqMailQueueFactoryTest.java | 22 +
29 files changed, 2346 insertions(+), 354 deletions(-)
create mode 100644 mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/NetworkErrorTest.java
create mode 100644 mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusDeadLetterQueueUpgradeTest.java
copy mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/SerializableMessage.java => server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala (87%)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 08/13: JAMES-3305 Extract
dispatchShouldWorkAfterNetworkIssuesForOldRegistration in its own class
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 c13a78a2cfde867622b186d1c14f38383b9f2ac3
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Sep 23 12:07:27 2020 +0700
JAMES-3305 Extract dispatchShouldWorkAfterNetworkIssuesForOldRegistration in its own class
Fails in the nested multi-eventBus environment of RabbitMQEventBusTest, succeeds in its own test class.
---
.../james/mailbox/events/NetworkErrorTest.java | 87 ++++++++++++++++++++++
.../james/mailbox/events/RabbitMQEventBusTest.java | 18 -----
2 files changed, 87 insertions(+), 18 deletions(-)
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/NetworkErrorTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/NetworkErrorTest.java
new file mode 100644
index 0000000..4d0ea38
--- /dev/null
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/NetworkErrorTest.java
@@ -0,0 +1,87 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mailbox.events;
+
+import static org.apache.james.mailbox.events.EventBusTestFixture.EVENT;
+import static org.apache.james.mailbox.events.EventBusTestFixture.GROUP_A;
+import static org.apache.james.mailbox.events.EventBusTestFixture.NO_KEYS;
+import static org.apache.james.mailbox.events.EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION;
+import static org.apache.james.mailbox.events.EventBusTestFixture.newListener;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.verify;
+
+import org.apache.james.backends.rabbitmq.RabbitMQExtension;
+import org.apache.james.backends.rabbitmq.RabbitMQFixture;
+import org.apache.james.event.json.EventSerializer;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
+import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class NetworkErrorTest {
+ @RegisterExtension
+ static RabbitMQExtension rabbitMQExtension = RabbitMQExtension.singletonRabbitMQ()
+ .isolationPolicy(RabbitMQExtension.IsolationPolicy.WEAK);
+
+ private RabbitMQEventBus eventBus;
+
+ @BeforeEach
+ void setUp() {
+ MemoryEventDeadLetters memoryEventDeadLetters = new MemoryEventDeadLetters();
+
+ TestId.Factory mailboxIdFactory = new TestId.Factory();
+ EventSerializer eventSerializer = new EventSerializer(mailboxIdFactory, new TestMessageId.Factory(), new DefaultUserQuotaRootResolver.DefaultQuotaRootDeserializer());
+ RoutingKeyConverter routingKeyConverter = RoutingKeyConverter.forFactories(new MailboxIdRegistrationKey.Factory(mailboxIdFactory));
+
+ eventBus = new RabbitMQEventBus(rabbitMQExtension.getSender(), rabbitMQExtension.getReceiverProvider(),
+ eventSerializer, RETRY_BACKOFF_CONFIGURATION, routingKeyConverter,
+ memoryEventDeadLetters, new RecordingMetricFactory(), rabbitMQExtension.getRabbitChannelPool());
+
+ eventBus.start();
+ }
+
+ @AfterEach
+ void tearDown() {
+ eventBus.stop();
+ }
+
+ @Test
+ void dispatchShouldWorkAfterNetworkIssuesForOldRegistration() {
+ MailboxListener listener = newListener();
+ eventBus.register(listener, GROUP_A);
+
+ rabbitMQExtension.getRabbitMQ().pause();
+
+ assertThatThrownBy(() -> eventBus.dispatch(EVENT, NO_KEYS).block())
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Retries exhausted");
+
+ rabbitMQExtension.getRabbitMQ().unpause();
+
+ eventBus.dispatch(EVENT, NO_KEYS).block();
+ RabbitMQFixture.awaitAtMostThirtySeconds
+ .untilAsserted(() -> verify(listener).event(EVENT));
+ }
+
+}
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
index 80650a9..7c8ee5f 100644
--- a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
@@ -460,24 +460,6 @@ class RabbitMQEventBusTest implements GroupContract.SingleEventBusGroupContract,
}
@Test
- void dispatchShouldWorkAfterNetworkIssuesForOldRegistration() {
- rabbitMQEventBusWithNetWorkIssue.start();
- MailboxListener listener = newListener();
- rabbitMQEventBusWithNetWorkIssue.register(listener, GROUP_A);
-
- rabbitMQNetWorkIssueExtension.getRabbitMQ().pause();
-
- assertThatThrownBy(() -> rabbitMQEventBusWithNetWorkIssue.dispatch(EVENT, NO_KEYS).block())
- .isInstanceOf(IllegalStateException.class)
- .hasMessageContaining("Retries exhausted");
-
- rabbitMQNetWorkIssueExtension.getRabbitMQ().unpause();
-
- rabbitMQEventBusWithNetWorkIssue.dispatch(EVENT, NO_KEYS).block();
- assertThatListenerReceiveOneEvent(listener);
- }
-
- @Test
void dispatchShouldWorkAfterNetworkIssuesForOldRegistrationAndKey() {
rabbitMQEventBusWithNetWorkIssue.start();
MailboxListener listener = newListener();
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 04/13: JAMES-3379 Email/get specific parsed
headers: asURLs
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 7ce20ab746f043b62c200bb0a557a452f2ed4217
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Sep 23 11:37:12 2020 +0700
JAMES-3379 Email/get specific parsed headers: asURLs
---
.../rfc8621/contract/EmailGetMethodContract.scala | 162 +++++++++++++++++++++
.../james/jmap/json/EmailGetSerializer.scala | 5 +-
.../scala/org/apache/james/jmap/mail/Email.scala | 16 +-
.../org/apache/james/jmap/mail/EmailHeader.scala | 20 ++-
4 files changed, 200 insertions(+), 3 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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index 59b4a52..61fb12f 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -6522,4 +6522,166 @@ trait EmailGetMethodContract {
| "header:Date:asDate": null
}""".stripMargin)
}
+
+ @Test
+ def emailGetShouldReturnSpecificHeaderAsURLs(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("List-Help", "<http://www.host.com/list/>, <ma...@host.com>"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:List-Help:asURLs"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:List-Help:asURLs": [
+ | "http://www.host.com/list/",
+ | "mailto:list-info@host.com"
+ | ]
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnSpecificHeaderAsURLsAndIgnoresComments(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("List-Help", "<http://www.host.com/list/>, (FTP) <ma...@host.com>"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:List-Help:asURLs"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:List-Help:asURLs": [
+ | "http://www.host.com/list/",
+ | "mailto:list-info@host.com"
+ | ]
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnNullWhenInvalidSpecificHeaderAsURLs(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("List-Help", "Invalid"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:List-Help:asURLs"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:List-Help:asURLs": null
+ }""".stripMargin)
+ }
}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index 6712ef1..9ec87f6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
package org.apache.james.jmap.json
import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValu [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValu [...]
import org.apache.james.jmap.model._
import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
import play.api.libs.functional.syntax._
@@ -65,6 +65,8 @@ object EmailGetSerializer {
private implicit val groupedAddressesHeaderWrites: Writes[GroupedAddressesHeaderValue] = Json.valueWrites[GroupedAddressesHeaderValue]
private implicit val messageIdsHeaderWrites: Writes[MessageIdsHeaderValue] = Json.valueWrites[MessageIdsHeaderValue]
private implicit val dateHeaderWrites: Writes[DateHeaderValue] = Json.valueWrites[DateHeaderValue]
+ private implicit val headerURLWrites: Writes[HeaderURL] = Json.valueWrites[HeaderURL]
+ private implicit val urlsHeaderWrites: Writes[URLsHeaderValue] = Json.valueWrites[URLsHeaderValue]
private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
case headerValue: RawHeaderValue => Json.toJson[RawHeaderValue](headerValue)
case headerValue: TextHeaderValue => Json.toJson[TextHeaderValue](headerValue)
@@ -72,6 +74,7 @@ object EmailGetSerializer {
case headerValue: GroupedAddressesHeaderValue => Json.toJson[GroupedAddressesHeaderValue](headerValue)
case headerValue: MessageIdsHeaderValue => Json.toJson[MessageIdsHeaderValue](headerValue)
case headerValue: DateHeaderValue => Json.toJson[DateHeaderValue](headerValue)
+ case headerValue: URLsHeaderValue => Json.toJson[URLsHeaderValue](headerValue)
}
private implicit val headersWrites: Writes[EmailHeader] = Json.writes[EmailHeader]
private implicit val bodyValueWrites: Writes[EmailBodyValue] = Json.writes[EmailBodyValue]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 62d225c..0653d80 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -34,6 +34,7 @@ import eu.timepit.refined.types.string.NonEmptyString
import javax.inject.Inject
import org.apache.james.jmap.api.model.Preview
import org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperties, MessageFastViewProjection}
+import org.apache.james.jmap.mail.BracketHeader.sanitize
import org.apache.james.jmap.mail.Email.{Size, sanitizeSize}
import org.apache.james.jmap.method.ZoneIdProvider
import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
@@ -149,8 +150,14 @@ case object FullReadLevel extends ReadLevel
object HeaderMessageId {
def from(string: String): HeaderMessageId = HeaderMessageId(sanitize(string))
+}
+
+object HeaderURL {
+ def from(string: String): HeaderURL = HeaderURL(sanitize(string))
+}
- private def sanitize(string: String): String = string match {
+object BracketHeader {
+ def sanitize(string: String): String = string match {
case s if s.startsWith("<") => sanitize(s.substring(1))
case s if s.endsWith(">") => sanitize(s.substring(0, s.length - 1))
case s => s
@@ -169,6 +176,7 @@ object ParseOptions {
case "asGroupedAddresses" => Some(AsGroupedAddresses)
case "asMessageIds" => Some(AsMessageIds)
case "asDate" => Some(AsDate)
+ case "asURLs" => Some(AsURLs)
case _ => None
}
}
@@ -196,6 +204,10 @@ case object AsDate extends ParseOption {
def extractHeaderValue(field: Field, zoneId: ZoneId): Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, zoneId))
}
+case object AsURLs extends ParseOption {
+ override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(URLsHeaderValue.from(field))
+}
+
case class HeaderMessageId(value: String) extends AnyVal
case class Subject(value: String) extends AnyVal
@@ -206,6 +218,8 @@ case class ThreadId(value: String) extends AnyVal
case class HasAttachment(value: Boolean) extends AnyVal
+case class HeaderURL(value: String) extends AnyVal
+
case class EmailMetadata(id: MessageId,
blobId: BlobId,
threadId: ThreadId,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 343f6dd..7f05aec 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -26,7 +26,7 @@ import org.apache.commons.lang3.StringUtils
import org.apache.james.jmap.model.UTCDate
import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
-import org.apache.james.mime4j.field.{AddressListFieldImpl, DateTimeFieldImpl}
+import org.apache.james.mime4j.field.{AddressListFieldImpl, ContentLocationFieldImpl, DateTimeFieldImpl}
import org.apache.james.mime4j.stream.Field
import org.apache.james.mime4j.util.MimeUtil
@@ -102,6 +102,23 @@ object DateHeaderValue extends EmailHeaderValue {
.getOrElse(DateHeaderValue(None))
}
+object URLsHeaderValue extends EmailHeaderValue {
+ def from(field: Field): URLsHeaderValue = {
+ val url: Option[List[HeaderURL]] = Option(ContentLocationFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getLocation)
+ .map(urls => urls.split(',')
+ .toList
+ .flatMap(url => {
+ if(url.startsWith("<") && url.endsWith(">")) {
+ scala.Right(HeaderURL.from(url))
+ } else {
+ Left()
+ }
+ }.toOption))
+
+ URLsHeaderValue(url.filter(_.nonEmpty))
+ }
+}
+
case class EmailHeaderName(value: String) extends AnyVal
sealed trait EmailHeaderValue
@@ -111,5 +128,6 @@ case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderVa
case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue
case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends EmailHeaderValue
case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
+case class URLsHeaderValue(value: Option[List[HeaderURL]]) extends EmailHeaderValue
case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 06/13: JAMES-3305 James do not start if eventBus
workQueues don't rely on dead-letter
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 25fa48948d8457e7ca38ddd6cd9c4193bcf85db5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Sep 22 11:52:50 2020 +0700
JAMES-3305 James do not start if eventBus workQueues don't rely on dead-letter
Difficulties encountered:
- The reactive AMQP java client do not allow to see if a queue already exist. We need
to actually try to create it, and resume the error to handle creation errors.
- The error causes the entire channel (sender) to be crashed. As such, the potentially
erroneous createQueue needs to be performed in a separate channel.
---
.../rabbitmq/ReactorRabbitMQChannelPool.java | 28 +++++++
.../james/mailbox/events/GroupRegistration.java | 17 +++--
.../mailbox/events/GroupRegistrationHandler.java | 7 +-
.../james/mailbox/events/RabbitMQEventBus.java | 9 ++-
...RabbitMQEventBusDeadLetterQueueUpgradeTest.java | 87 ++++++++++++++++++++++
.../james/mailbox/events/RabbitMQEventBusTest.java | 5 +-
.../rabbitmq/host/RabbitMQEventBusHostSystem.java | 3 +-
7 files changed, 141 insertions(+), 15 deletions(-)
diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/ReactorRabbitMQChannelPool.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/ReactorRabbitMQChannelPool.java
index 913d84d..1de99d3 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/ReactorRabbitMQChannelPool.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/ReactorRabbitMQChannelPool.java
@@ -37,13 +37,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.fge.lambdas.Throwing;
+import com.google.common.base.Preconditions;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ShutdownSignalException;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;
import reactor.core.scheduler.Schedulers;
+import reactor.rabbitmq.BindingSpecification;
import reactor.rabbitmq.ChannelPool;
+import reactor.rabbitmq.QueueSpecification;
import reactor.rabbitmq.RabbitFlux;
import reactor.rabbitmq.Receiver;
import reactor.rabbitmq.ReceiverOptions;
@@ -236,6 +241,29 @@ public class ReactorRabbitMQChannelPool implements ChannelPool, Startable {
connectionMono.map(Throwing.function(Connection::createChannel)).cache()));
}
+ public Mono<Void> createWorkQueue(QueueSpecification queueSpecification, BindingSpecification bindingSpecification) {
+ Preconditions.checkArgument(queueSpecification.getName() != null, "WorkQueue pattern do not make sense for unnamed queues");
+ Preconditions.checkArgument(queueSpecification.getName().equals(bindingSpecification.getQueue()),
+ "Binding needs to be targetting the created queue %s instead of %s",
+ queueSpecification.getName(), bindingSpecification.getQueue());
+
+ return Flux.concat(
+ Mono.using(this::createSender,
+ managementSender -> managementSender.declareQueue(queueSpecification),
+ Sender::close)
+ .onErrorResume(
+ e -> e instanceof ShutdownSignalException
+ && e.getMessage().contains("reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'x-dead-letter-exchange' for queue"),
+ e -> {
+ LOGGER.warn("{} already exists without dead-letter setup. Dead lettered messages to it will be lost. " +
+ "To solve this, re-create the queue with the x-dead-letter-exchange argument set up.",
+ queueSpecification.getName());
+ return Mono.empty();
+ }),
+ sender.bind(bindingSpecification))
+ .then();
+ }
+
private void invalidateObject(Channel channel) {
try {
pool.invalidateObject(channel);
diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistration.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistration.java
index 5141ae5..99c5cb9 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistration.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistration.java
@@ -34,6 +34,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
+import org.apache.james.backends.rabbitmq.ReactorRabbitMQChannelPool;
import org.apache.james.backends.rabbitmq.ReceiverProvider;
import org.apache.james.event.json.EventSerializer;
import org.apache.james.util.MDCBuilder;
@@ -43,7 +44,6 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import reactor.core.Disposable;
-import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.rabbitmq.AcknowledgableDelivery;
@@ -78,6 +78,7 @@ class GroupRegistration implements Registration {
static final String RETRY_COUNT = "retry-count";
static final int DEFAULT_RETRY_COUNT = 0;
+ private final ReactorRabbitMQChannelPool channelPool;
private final MailboxListener.ReactiveMailboxListener mailboxListener;
private final WorkQueueName queueName;
private final Receiver receiver;
@@ -91,10 +92,11 @@ class GroupRegistration implements Registration {
private final MailboxListenerExecutor mailboxListenerExecutor;
private Optional<Disposable> receiverSubscriber;
- GroupRegistration(Sender sender, ReceiverProvider receiverProvider, EventSerializer eventSerializer,
+ GroupRegistration(ReactorRabbitMQChannelPool channelPool, Sender sender, ReceiverProvider receiverProvider, EventSerializer eventSerializer,
MailboxListener.ReactiveMailboxListener mailboxListener, Group group, RetryBackoffConfiguration retryBackoff,
EventDeadLetters eventDeadLetters,
Runnable unregisterGroup, MailboxListenerExecutor mailboxListenerExecutor) {
+ this.channelPool = channelPool;
this.eventSerializer = eventSerializer;
this.mailboxListener = mailboxListener;
this.queueName = WorkQueueName.of(group);
@@ -120,17 +122,16 @@ class GroupRegistration implements Registration {
}
private Mono<Void> createGroupWorkQueue() {
- return Flux.concat(
- sender.declareQueue(QueueSpecification.queue(queueName.asString())
+ return channelPool.createWorkQueue(
+ QueueSpecification.queue(queueName.asString())
.durable(DURABLE)
.exclusive(!EXCLUSIVE)
.autoDelete(!AUTO_DELETE)
- .arguments(deadLetterQueue(MAILBOX_EVENT_DEAD_LETTER_EXCHANGE_NAME))),
- sender.bind(BindingSpecification.binding()
+ .arguments(deadLetterQueue(MAILBOX_EVENT_DEAD_LETTER_EXCHANGE_NAME)),
+ BindingSpecification.binding()
.exchange(MAILBOX_EVENT_EXCHANGE_NAME)
.queue(queueName.asString())
- .routingKey(EMPTY_ROUTING_KEY)))
- .then();
+ .routingKey(EMPTY_ROUTING_KEY));
}
private Disposable consumeWorkQueue() {
diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistrationHandler.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistrationHandler.java
index 3f729e0..251b375 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistrationHandler.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/GroupRegistrationHandler.java
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.james.backends.rabbitmq.ReactorRabbitMQChannelPool;
import org.apache.james.backends.rabbitmq.ReceiverProvider;
import org.apache.james.event.json.EventSerializer;
@@ -31,16 +32,18 @@ import reactor.rabbitmq.Sender;
class GroupRegistrationHandler {
private final Map<Group, GroupRegistration> groupRegistrations;
private final EventSerializer eventSerializer;
+ private final ReactorRabbitMQChannelPool channelPool;
private final Sender sender;
private final ReceiverProvider receiverProvider;
private final RetryBackoffConfiguration retryBackoff;
private final EventDeadLetters eventDeadLetters;
private final MailboxListenerExecutor mailboxListenerExecutor;
- GroupRegistrationHandler(EventSerializer eventSerializer, Sender sender, ReceiverProvider receiverProvider,
+ GroupRegistrationHandler(EventSerializer eventSerializer, ReactorRabbitMQChannelPool channelPool, Sender sender, ReceiverProvider receiverProvider,
RetryBackoffConfiguration retryBackoff,
EventDeadLetters eventDeadLetters, MailboxListenerExecutor mailboxListenerExecutor) {
this.eventSerializer = eventSerializer;
+ this.channelPool = channelPool;
this.sender = sender;
this.receiverProvider = receiverProvider;
this.retryBackoff = retryBackoff;
@@ -71,7 +74,7 @@ class GroupRegistrationHandler {
private GroupRegistration newGroupRegistration(MailboxListener.ReactiveMailboxListener listener, Group group) {
return new GroupRegistration(
- sender,
+ channelPool, sender,
receiverProvider,
eventSerializer,
listener,
diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
index 29dcc6f..5fb8be6 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
@@ -24,6 +24,7 @@ import java.util.Set;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
+import org.apache.james.backends.rabbitmq.ReactorRabbitMQChannelPool;
import org.apache.james.backends.rabbitmq.ReceiverProvider;
import org.apache.james.event.json.EventSerializer;
import org.apache.james.lifecycle.api.Startable;
@@ -53,6 +54,7 @@ public class RabbitMQEventBus implements EventBus, Startable {
private final MailboxListenerExecutor mailboxListenerExecutor;
private final Sender sender;
private final ReceiverProvider receiverProvider;
+ private final ReactorRabbitMQChannelPool channelPool;
private volatile boolean isRunning;
private volatile boolean isStopping;
@@ -64,10 +66,11 @@ public class RabbitMQEventBus implements EventBus, Startable {
public RabbitMQEventBus(Sender sender, ReceiverProvider receiverProvider, EventSerializer eventSerializer,
RetryBackoffConfiguration retryBackoff,
RoutingKeyConverter routingKeyConverter,
- EventDeadLetters eventDeadLetters, MetricFactory metricFactory) {
+ EventDeadLetters eventDeadLetters, MetricFactory metricFactory, ReactorRabbitMQChannelPool channelPool) {
this.sender = sender;
this.receiverProvider = receiverProvider;
this.mailboxListenerExecutor = new MailboxListenerExecutor(metricFactory);
+ this.channelPool = channelPool;
this.eventBusId = EventBusId.random();
this.eventSerializer = eventSerializer;
this.routingKeyConverter = routingKeyConverter;
@@ -82,7 +85,7 @@ public class RabbitMQEventBus implements EventBus, Startable {
LocalListenerRegistry localListenerRegistry = new LocalListenerRegistry();
keyRegistrationHandler = new KeyRegistrationHandler(eventBusId, eventSerializer, sender, receiverProvider, routingKeyConverter, localListenerRegistry, mailboxListenerExecutor, retryBackoff);
- groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
+ groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, channelPool, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor, eventDeadLetters);
eventDispatcher.start();
@@ -97,7 +100,7 @@ public class RabbitMQEventBus implements EventBus, Startable {
LocalListenerRegistry localListenerRegistry = new LocalListenerRegistry();
keyRegistrationHandler = new KeyRegistrationHandler(eventBusId, eventSerializer, sender, receiverProvider, routingKeyConverter, localListenerRegistry, mailboxListenerExecutor, retryBackoff);
- groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
+ groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, channelPool, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor, eventDeadLetters);
keyRegistrationHandler.declareQueue();
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusDeadLetterQueueUpgradeTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusDeadLetterQueueUpgradeTest.java
new file mode 100644
index 0000000..89c64cc
--- /dev/null
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusDeadLetterQueueUpgradeTest.java
@@ -0,0 +1,87 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mailbox.events;
+
+import static org.apache.james.backends.rabbitmq.Constants.AUTO_DELETE;
+import static org.apache.james.backends.rabbitmq.Constants.DURABLE;
+import static org.apache.james.backends.rabbitmq.Constants.EXCLUSIVE;
+import static org.apache.james.mailbox.events.EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import org.apache.james.backends.rabbitmq.RabbitMQExtension;
+import org.apache.james.event.json.EventSerializer;
+import org.apache.james.mailbox.events.EventBusTestFixture.GroupA;
+import org.apache.james.mailbox.events.GroupRegistration.WorkQueueName;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
+import org.apache.james.mailbox.util.EventCollector;
+import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import reactor.rabbitmq.QueueSpecification;
+
+class RabbitMQEventBusDeadLetterQueueUpgradeTest {
+ @RegisterExtension
+ static RabbitMQExtension rabbitMQExtension = RabbitMQExtension.singletonRabbitMQ()
+ .isolationPolicy(RabbitMQExtension.IsolationPolicy.WEAK);
+
+ private RabbitMQEventBus eventBus;
+
+ @BeforeEach
+ void setUp() {
+ MemoryEventDeadLetters memoryEventDeadLetters = new MemoryEventDeadLetters();
+
+ TestId.Factory mailboxIdFactory = new TestId.Factory();
+ EventSerializer eventSerializer = new EventSerializer(mailboxIdFactory, new TestMessageId.Factory(), new DefaultUserQuotaRootResolver.DefaultQuotaRootDeserializer());
+ RoutingKeyConverter routingKeyConverter = RoutingKeyConverter.forFactories(new MailboxIdRegistrationKey.Factory(mailboxIdFactory));
+
+ eventBus = new RabbitMQEventBus(rabbitMQExtension.getSender(), rabbitMQExtension.getReceiverProvider(),
+ eventSerializer, RETRY_BACKOFF_CONFIGURATION, routingKeyConverter,
+ memoryEventDeadLetters, new RecordingMetricFactory(), rabbitMQExtension.getRabbitChannelPool());
+
+ eventBus.start();
+ }
+
+ @AfterEach
+ void tearDown() {
+ eventBus.stop();
+ }
+
+ @Test
+ void eventBusShouldStartWhenDeadLetterUpgradeWasNotPerformed() {
+ GroupA registeredGroup = new GroupA();
+ WorkQueueName workQueueName = WorkQueueName.of(registeredGroup);
+
+ rabbitMQExtension.getSender()
+ .declareQueue(QueueSpecification.queue(workQueueName.asString())
+ .durable(DURABLE)
+ .exclusive(!EXCLUSIVE)
+ .autoDelete(!AUTO_DELETE))
+ .block();
+
+ assertThatCode(eventBus::start).doesNotThrowAnyException();
+ assertThatCode(() -> eventBus.register(new EventCollector(), registeredGroup)).doesNotThrowAnyException();
+ }
+
+}
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
index 78079d5..80650a9 100644
--- a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
@@ -154,7 +154,10 @@ class RabbitMQEventBusTest implements GroupContract.SingleEventBusGroupContract,
}
private RabbitMQEventBus newEventBus(Sender sender, ReceiverProvider receiverProvider) {
- return new RabbitMQEventBus(sender, receiverProvider, eventSerializer, EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, routingKeyConverter, memoryEventDeadLetters, new RecordingMetricFactory());
+ return new RabbitMQEventBus(sender, receiverProvider, eventSerializer,
+ EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, routingKeyConverter,
+ memoryEventDeadLetters, new RecordingMetricFactory(),
+ rabbitMQExtension.getRabbitChannelPool());
}
@Override
diff --git a/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java b/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java
index 70128f3..7923b37 100644
--- a/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java
+++ b/mpt/impl/imap-mailbox/rabbitmq/src/test/java/org/apache/james/mpt/imapmailbox/rabbitmq/host/RabbitMQEventBusHostSystem.java
@@ -112,7 +112,8 @@ public class RabbitMQEventBusHostSystem extends JamesImapHostSystem {
RoutingKeyConverter routingKeyConverter = new RoutingKeyConverter(ImmutableSet.of(new MailboxIdRegistrationKey.Factory(mailboxIdFactory)));
return new RabbitMQEventBus(reactorRabbitMQChannelPool.getSender(), reactorRabbitMQChannelPool::createReceiver,
eventSerializer, RetryBackoffConfiguration.DEFAULT, routingKeyConverter, new MemoryEventDeadLetters(),
- new RecordingMetricFactory());
+ new RecordingMetricFactory(),
+ reactorRabbitMQChannelPool);
}
@Override
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 03/13: JAMES-3379 Email/get specific parsed
headers: asDate
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 00441ebc676176ff290287a5249a35c908fa8951
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Tue Sep 22 18:36:56 2020 +0700
JAMES-3379 Email/get specific parsed headers: asDate
---
.../rfc8621/contract/EmailGetMethodContract.scala | 107 +++++++++++++++++++++
.../james/jmap/json/EmailGetSerializer.scala | 4 +-
.../scala/org/apache/james/jmap/mail/Email.scala | 18 ++--
.../org/apache/james/jmap/mail/EmailGet.scala | 13 ++-
.../org/apache/james/jmap/mail/EmailHeader.scala | 11 ++-
5 files changed, 142 insertions(+), 11 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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index f266760..59b4a52 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -6415,4 +6415,111 @@ trait EmailGetMethodContract {
| ]
}""".stripMargin)
}
+
+ @Test
+ def emailGetShouldReturnSpecificHeaderAsDate(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("Date",
+ "Wed, 9 Sep 2020 07:00:26 +0200"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:Date:asDate"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:Date:asDate": "2020-09-09T05:00:26Z"
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnNullWhenInvalidSpecificHeaderAsDate(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("Date", "Invalid"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:Date:asDate"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:Date:asDate": null
+ }""".stripMargin)
+ }
}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index f937c44..6712ef1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
package org.apache.james.jmap.json
import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, GroupName, Gr [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValu [...]
import org.apache.james.jmap.model._
import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
import play.api.libs.functional.syntax._
@@ -64,12 +64,14 @@ object EmailGetSerializer {
"addresses" -> Json.toJson(o.addresses))
private implicit val groupedAddressesHeaderWrites: Writes[GroupedAddressesHeaderValue] = Json.valueWrites[GroupedAddressesHeaderValue]
private implicit val messageIdsHeaderWrites: Writes[MessageIdsHeaderValue] = Json.valueWrites[MessageIdsHeaderValue]
+ private implicit val dateHeaderWrites: Writes[DateHeaderValue] = Json.valueWrites[DateHeaderValue]
private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
case headerValue: RawHeaderValue => Json.toJson[RawHeaderValue](headerValue)
case headerValue: TextHeaderValue => Json.toJson[TextHeaderValue](headerValue)
case headerValue: AddressesHeaderValue => Json.toJson[AddressesHeaderValue](headerValue)
case headerValue: GroupedAddressesHeaderValue => Json.toJson[GroupedAddressesHeaderValue](headerValue)
case headerValue: MessageIdsHeaderValue => Json.toJson[MessageIdsHeaderValue](headerValue)
+ case headerValue: DateHeaderValue => Json.toJson[DateHeaderValue](headerValue)
}
private implicit val headersWrites: Writes[EmailHeader] = Json.writes[EmailHeader]
private implicit val bodyValueWrites: Writes[EmailBodyValue] = Json.writes[EmailBodyValue]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index de92da1..62d225c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -168,6 +168,7 @@ object ParseOptions {
case "asAddresses" => Some(AsAddresses)
case "asGroupedAddresses" => Some(AsGroupedAddresses)
case "asMessageIds" => Some(AsMessageIds)
+ case "asDate" => Some(AsDate)
case _ => None
}
}
@@ -190,7 +191,11 @@ case object AsGroupedAddresses extends ParseOption {
case object AsMessageIds extends ParseOption {
override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(MessageIdsHeaderValue.from(field))
}
+case object AsDate extends ParseOption {
+ override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, ZoneId.systemDefault()))
+ def extractHeaderValue(field: Field, zoneId: ZoneId): Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, zoneId))
+}
case class HeaderMessageId(value: String) extends AnyVal
case class Subject(value: String) extends AnyVal
@@ -228,10 +233,10 @@ object EmailHeaders {
sentAt = extractDate(mime4JMessage, "Date").map(date => UTCDate.from(date, zoneId)))
}
- def extractSpecificHeaders(properties: Option[Properties])(mime4JMessage: Message) = {
+ def extractSpecificHeaders(properties: Option[Properties])(zoneId: ZoneId, mime4JMessage: Message) = {
properties.getOrElse(Properties.empty()).value
.flatMap(property => SpecificHeaderRequest.from(property).toOption)
- .map(_.retrieveHeader(mime4JMessage))
+ .map(_.retrieveHeader(zoneId, mime4JMessage))
.toMap
}
@@ -272,7 +277,7 @@ object EmailHeaders {
private def extractDate(mime4JMessage: Message, fieldName: String): Option[Date] =
extractLastField(mime4JMessage, fieldName)
.flatMap {
- case f: DateTimeField => Some(f.getDate)
+ case f: DateTimeField => Option(f.getDate)
case _ => None
}
@@ -424,10 +429,9 @@ private class EmailHeaderViewFactory @Inject()(zoneIdProvider: ZoneIdProvider) e
size = sanitizeSize(firstMessage.getSize),
keywords = keywords),
header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
- specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+ specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), mime4JMessage))
}
}
-
}
private class EmailFullViewFactory @Inject()(zoneIdProvider: ZoneIdProvider, previewFactory: Preview.Factory) extends EmailViewFactory[EmailFullView] {
@@ -468,7 +472,7 @@ private class EmailFullViewFactory @Inject()(zoneIdProvider: ZoneIdProvider, pre
htmlBody = bodyStructure.htmlBody,
attachments = bodyStructure.attachments,
bodyValues = bodyValues),
- specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+ specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), mime4JMessage))
}
}
@@ -612,7 +616,7 @@ private class EmailFastViewReader @Inject()(messageIdManager: MessageIdManager,
hasAttachment = HasAttachment(fastView.hasAttachment),
preview = fastView.getPreview),
header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
- specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+ specificHeaders = EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), mime4JMessage))
}
}
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index 43e8c84..ea49946 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -19,6 +19,8 @@
package org.apache.james.jmap.mail
+import java.time.ZoneId
+
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric.NonNegative
@@ -30,6 +32,7 @@ import org.apache.james.jmap.model.State.State
import org.apache.james.jmap.model.{AccountId, Properties}
import org.apache.james.mime4j.dom.Message
import org.apache.james.mime4j.stream.Field
+
import scala.jdk.CollectionConverters._
case class EmailIds(value: List[UnparsedEmailId])
@@ -83,11 +86,17 @@ case class EmailGetResponse(accountId: AccountId,
notFound: EmailNotFound)
case class SpecificHeaderRequest(headerName: NonEmptyString, property: String, parseOption: Option[ParseOption]) {
- def retrieveHeader(message: Message): (String, Option[EmailHeaderValue]) = {
+ def retrieveHeader(zoneId: ZoneId, message: Message): (String, Option[EmailHeaderValue]) = {
val field: Option[Field] = Option(message.getHeader.getFields(property))
.map(_.asScala)
.flatMap(fields => fields.reverse.headOption)
- (headerName, field.flatMap(parseOption.getOrElse(AsRaw).extractHeaderValue(_)))
+ (headerName, field.flatMap({
+ val option = parseOption.getOrElse(AsRaw)
+ option match {
+ case AsDate => AsDate.extractHeaderValue(_, zoneId)
+ case _ => option.extractHeaderValue
+ }
+ }))
}
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 58db534..343f6dd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -20,9 +20,10 @@
package org.apache.james.jmap.mail
import java.nio.charset.StandardCharsets.US_ASCII
-import java.util.Date
+import java.time.ZoneId
import org.apache.commons.lang3.StringUtils
+import org.apache.james.jmap.model.UTCDate
import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
import org.apache.james.mime4j.field.{AddressListFieldImpl, DateTimeFieldImpl}
@@ -94,6 +95,13 @@ object MessageIdsHeaderValue {
}
}
+object DateHeaderValue extends EmailHeaderValue {
+ def from(field: Field, zoneId: ZoneId): DateHeaderValue =
+ Option(DateTimeFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getDate)
+ .map(date => DateHeaderValue(Some(UTCDate.from(date, zoneId))))
+ .getOrElse(DateHeaderValue(None))
+}
+
case class EmailHeaderName(value: String) extends AnyVal
sealed trait EmailHeaderValue
@@ -102,5 +110,6 @@ case class TextHeaderValue(value: String) extends EmailHeaderValue
case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderValue
case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue
case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends EmailHeaderValue
+case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 09/13: JAMES-3375 MailboxManager::search should
enable finner grained control over the search mailboxes
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 871d5f233225ce56be6e71bc99f973a9c01598f0
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Sep 23 13:50:30 2020 +0700
JAMES-3375 MailboxManager::search should enable finner grained control over the search mailboxes
Enable to either:
- Search personal mailboxes
- Search all accessible mailboxes
In addition to the existing inMailboxes/notInMailboxes criteria
---
.../mailbox/model/MultimailboxesSearchQuery.java | 74 +++-
.../apache/james/mailbox/MailboxManagerTest.java | 68 ++++
.../model/MultimailboxesSearchQueryTest.java | 13 +-
.../james/mailbox/store/StoreMailboxManager.java | 24 +-
.../contract/EmailQueryMethodContract.scala | 398 ++++++++++++++++++++-
.../james/jmap/method/EmailQueryMethod.scala | 14 +-
.../james/jmap/utils/search/MailboxFilter.scala | 13 +-
7 files changed, 571 insertions(+), 33 deletions(-)
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
index b0b867d..540f05f 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
@@ -21,6 +21,11 @@ package org.apache.james.mailbox.model;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.search.MailboxQuery;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
@@ -31,18 +36,67 @@ public class MultimailboxesSearchQuery {
public static Builder from(SearchQuery searchQuery) {
return new Builder(searchQuery);
}
+
+ public interface Namespace {
+ boolean keepAccessible(Mailbox mailbox);
+
+ MailboxQuery associatedMailboxSearchQuery();
+ }
+
+ public static class PersonalNamespace implements Namespace {
+ private final MailboxSession session;
+
+ public PersonalNamespace(MailboxSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public boolean keepAccessible(Mailbox mailbox) {
+ return mailbox.generateAssociatedPath().belongsTo(session);
+ }
+
+ @Override
+ public MailboxQuery associatedMailboxSearchQuery() {
+ return MailboxQuery.privateMailboxesBuilder(session)
+ .matchesAllMailboxNames()
+ .build();
+ }
+ }
+
+ public static class AccessibleNamespace implements Namespace {
+ @Override
+ public boolean keepAccessible(Mailbox mailbox) {
+ return true;
+ }
+
+ @Override
+ public MailboxQuery associatedMailboxSearchQuery() {
+ return MailboxQuery.builder().matchesAllMailboxNames().build();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(AccessibleNamespace.class);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof AccessibleNamespace;
+ }
+ }
public static class Builder {
-
private final SearchQuery searchQuery;
private ImmutableSet.Builder<MailboxId> mailboxIds;
private ImmutableSet.Builder<MailboxId> notInMailboxIds;
+ private Optional<Namespace> namespace;
private Builder(SearchQuery searchQuery) {
Preconditions.checkNotNull(searchQuery);
this.searchQuery = searchQuery;
this.mailboxIds = ImmutableSet.builder();
this.notInMailboxIds = ImmutableSet.builder();
+ this.namespace = Optional.empty();
}
public Builder inMailboxes(Collection<MailboxId> mailboxIds) {
@@ -58,13 +112,21 @@ public class MultimailboxesSearchQuery {
this.notInMailboxIds.addAll(mailboxIds);
return this;
}
+
+ public Builder inNamespace(Namespace namespace) {
+ this.namespace = Optional.of(namespace);
+ return this;
+ }
public Builder notInMailboxes(MailboxId... mailboxIds) {
return notInMailboxes(Arrays.asList(mailboxIds));
}
public MultimailboxesSearchQuery build() {
- return new MultimailboxesSearchQuery(searchQuery, mailboxIds.build(), notInMailboxIds.build());
+ return new MultimailboxesSearchQuery(searchQuery,
+ mailboxIds.build(),
+ notInMailboxIds.build(),
+ namespace.orElse(new AccessibleNamespace()));
}
}
@@ -72,12 +134,14 @@ public class MultimailboxesSearchQuery {
private final SearchQuery searchQuery;
private final ImmutableSet<MailboxId> inMailboxes;
private final ImmutableSet<MailboxId> notInMailboxes;
+ private final Namespace namespace;
@VisibleForTesting
- MultimailboxesSearchQuery(SearchQuery searchQuery, ImmutableSet<MailboxId> inMailboxes, ImmutableSet<MailboxId> notInMailboxes) {
+ MultimailboxesSearchQuery(SearchQuery searchQuery, ImmutableSet<MailboxId> inMailboxes, ImmutableSet<MailboxId> notInMailboxes, Namespace namespace) {
this.searchQuery = searchQuery;
this.inMailboxes = inMailboxes;
this.notInMailboxes = notInMailboxes;
+ this.namespace = namespace;
}
public ImmutableSet<MailboxId> getInMailboxes() {
@@ -91,4 +155,8 @@ public class MultimailboxesSearchQuery {
public SearchQuery getSearchQuery() {
return searchQuery;
}
+
+ public Namespace getNamespace() {
+ return namespace;
+ }
}
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index 95a220a..a274329 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -76,6 +76,8 @@ import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MessageResult;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery.AccessibleNamespace;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery.PersonalNamespace;
import org.apache.james.mailbox.model.Quota;
import org.apache.james.mailbox.model.QuotaRoot;
import org.apache.james.mailbox.model.SearchQuery;
@@ -1256,6 +1258,39 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
}
@Test
+ void searchForMessageShouldReturnMessagesFromMyDelegatedMailboxesWhenAccessibleNamespace() throws Exception {
+ assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL));
+
+ session = mailboxManager.createSystemSession(USER_1);
+ MailboxSession sessionFromDelegater = mailboxManager.createSystemSession(USER_2);
+ MailboxPath delegatedMailboxPath = MailboxPath.forUser(USER_2, "SHARED");
+ MailboxId delegatedMailboxId = mailboxManager.createMailbox(delegatedMailboxPath, sessionFromDelegater).get();
+ MessageManager delegatedMessageManager = mailboxManager.getMailbox(delegatedMailboxId, sessionFromDelegater);
+
+ MessageId messageId = delegatedMessageManager
+ .appendMessage(AppendCommand.from(message), sessionFromDelegater)
+ .getId().getMessageId();
+
+ mailboxManager.setRights(delegatedMailboxPath,
+ MailboxACL.EMPTY.apply(MailboxACL.command()
+ .forUser(USER_1)
+ .rights(MailboxACL.Right.Read, MailboxACL.Right.Lookup)
+ .asAddition()),
+ sessionFromDelegater);
+
+ MultimailboxesSearchQuery multiMailboxesQuery = MultimailboxesSearchQuery
+ .from(SearchQuery.matchAll())
+ .inNamespace(new AccessibleNamespace())
+ .build();
+
+ assertThat(
+ Flux.from(mailboxManager.search(multiMailboxesQuery, session, DEFAULT_MAXIMUM_LIMIT))
+ .collectList()
+ .block())
+ .containsOnly(messageId);
+ }
+
+ @Test
void searchForMessageShouldNotReturnMessagesFromMyDelegatedMailboxesICanNotRead() throws Exception {
assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL));
@@ -1370,6 +1405,39 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
}
@Test
+ void searchShouldRestrictResultsToTheSuppliedUserNamespace() throws Exception {
+ assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL));
+
+ session = mailboxManager.createSystemSession(USER_1);
+ MailboxSession sessionFromDelegater = mailboxManager.createSystemSession(USER_2);
+ MailboxPath delegatedMailboxPath = MailboxPath.forUser(USER_2, "SHARED");
+ MailboxId delegatedMailboxId = mailboxManager.createMailbox(delegatedMailboxPath, sessionFromDelegater).get();
+ MessageManager delegatedMessageManager = mailboxManager.getMailbox(delegatedMailboxId, sessionFromDelegater);
+
+ MessageId messageId = delegatedMessageManager
+ .appendMessage(AppendCommand.from(message), sessionFromDelegater)
+ .getId().getMessageId();
+
+ mailboxManager.setRights(delegatedMailboxPath,
+ MailboxACL.EMPTY.apply(MailboxACL.command()
+ .forUser(USER_1)
+ .rights(MailboxACL.Right.Read, MailboxACL.Right.Lookup)
+ .asAddition()),
+ sessionFromDelegater);
+
+ MultimailboxesSearchQuery multiMailboxesQuery = MultimailboxesSearchQuery
+ .from(SearchQuery.matchAll())
+ .inNamespace(new PersonalNamespace(session))
+ .build();
+
+ assertThat(
+ Flux.from(mailboxManager.search(multiMailboxesQuery, session, DEFAULT_MAXIMUM_LIMIT))
+ .collectList()
+ .block())
+ .isEmpty();
+ }
+
+ @Test
void searchForMessageShouldOnlySearchInGivenMailbox() throws Exception {
assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL));
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MultimailboxesSearchQueryTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MultimailboxesSearchQueryTest.java
index 6d50cf0..d932a14 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MultimailboxesSearchQueryTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MultimailboxesSearchQueryTest.java
@@ -21,6 +21,7 @@ package org.apache.james.mailbox.model;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery.AccessibleNamespace;
import org.junit.jupiter.api.Test;
import com.google.common.collect.ImmutableSet;
@@ -42,7 +43,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWheninMailboxes() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of();
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of();
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).build();
assertThat(actual).isEqualToComparingFieldByField(expected);
@@ -52,7 +53,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWhenEmptyMailboxes() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of();
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of();
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).inMailboxes().build();
assertThat(actual).isEqualToComparingFieldByField(expected);
@@ -62,7 +63,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWhenEmptyNotInMailboxes() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of();
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of();
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).notInMailboxes().build();
assertThat(actual).isEqualToComparingFieldByField(expected);
@@ -73,7 +74,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWhenOneMailbox() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of(ID_1);
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of();
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).inMailboxes(ID_1).build();
assertThat(actual).isEqualToComparingFieldByField(expected);
@@ -83,7 +84,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWhenOneNotInMailbox() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of();
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of(ID_1);
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).notInMailboxes(ID_1).build();
assertThat(actual).isEqualToComparingFieldByField(expected);
@@ -94,7 +95,7 @@ class MultimailboxesSearchQueryTest {
void buildShouldBuildWhenAllDefined() {
ImmutableSet<MailboxId> inMailboxes = ImmutableSet.of(ID_1);
ImmutableSet<MailboxId> notInMailboxes = ImmutableSet.of(ID_2);
- MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes);
+ MultimailboxesSearchQuery expected = new MultimailboxesSearchQuery(EMPTY_QUERY, inMailboxes, notInMailboxes, new AccessibleNamespace());
MultimailboxesSearchQuery actual = MultimailboxesSearchQuery.from(EMPTY_QUERY).inMailboxes(ID_1).notInMailboxes(ID_2).build();
assertThat(actual).isEqualToComparingFieldByField(expected);
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index faf4d50..52e66c9 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -696,32 +696,32 @@ public class StoreMailboxManager implements MailboxManager {
@Override
public Flux<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException {
- return getInMailboxes(expression.getInMailboxes(), session)
- .filter(id -> !expression.getNotInMailboxes().contains(id))
+ return getInMailboxes(expression, session)
+ .filter(id -> !expression.getNotInMailboxes().contains(id.getMailboxId()))
+ .filter(mailbox -> expression.getNamespace().keepAccessible(mailbox))
+ .map(Mailbox::getMailboxId)
.collect(Guavate.toImmutableSet())
.flatMapMany(Throwing.function(ids -> index.search(session, ids, expression.getSearchQuery(), limit)));
}
- private Flux<MailboxId> getInMailboxes(ImmutableSet<MailboxId> inMailboxes, MailboxSession session) throws MailboxException {
- if (inMailboxes.isEmpty()) {
- return getAllReadableMailbox(session);
+ private Flux<Mailbox> getInMailboxes(MultimailboxesSearchQuery expression, MailboxSession session) throws MailboxException {
+ if (expression.getInMailboxes().isEmpty()) {
+ return searchMailboxes(expression.getNamespace().associatedMailboxSearchQuery(), session, Right.Read);
} else {
- return filterReadable(inMailboxes, session);
+ return filterReadable(expression.getInMailboxes(), session);
}
}
- private Flux<MailboxId> getAllReadableMailbox(MailboxSession session) throws MailboxException {
- return searchMailboxes(MailboxQuery.builder().matchesAllMailboxNames().build(), session, Right.Read)
- .map(Mailbox::getMailboxId);
+ private Flux<Mailbox> getAllReadableMailbox(MailboxQuery mailboxQuery, MailboxSession session) throws MailboxException {
+ return searchMailboxes(mailboxQuery, session, Right.Read);
}
- private Flux<MailboxId> filterReadable(ImmutableSet<MailboxId> inMailboxes, MailboxSession session) throws MailboxException {
+ private Flux<Mailbox> filterReadable(ImmutableSet<MailboxId> inMailboxes, MailboxSession session) throws MailboxException {
MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session);
return Flux.fromIterable(inMailboxes)
.concatMap(mailboxMapper::findMailboxById)
- .filter(Throwing.<Mailbox>predicate(mailbox -> storeRightManager.hasRight(mailbox, Right.Read, session)).sneakyThrow())
- .map(Mailbox::getMailboxId);
+ .filter(Throwing.<Mailbox>predicate(mailbox -> storeRightManager.hasRight(mailbox, Right.Read, session)).sneakyThrow());
}
@Override
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 e521a7f..3b5a1f9 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
@@ -41,11 +41,12 @@ import org.apache.james.jmap.http.UserCredential
import org.apache.james.jmap.model.UTCDate
import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ANDRE, ANDRE_PASSWORD, BOB, BOB_PASSWORD, DOMAIN, authScheme, baseRequestSpecBuilder}
import org.apache.james.mailbox.MessageManager.AppendCommand
+import org.apache.james.mailbox.model.MailboxACL.Right
import org.apache.james.mailbox.model.MailboxPath.inbox
-import org.apache.james.mailbox.model.{MailboxPath, MessageId}
+import org.apache.james.mailbox.model.{MailboxACL, MailboxPath, MessageId}
import org.apache.james.mime4j.dom.Message
import org.apache.james.mime4j.message.DefaultMessageWriter
-import org.apache.james.modules.MailboxProbeImpl
+import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl}
import org.apache.james.utils.DataProbeImpl
import org.awaitility.Awaitility
import org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS
@@ -156,6 +157,395 @@ trait EmailQueryMethodContract {
}
@Test
+ def emailInSharedMailboxesShouldNotBeDisplayedWhenNoExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6"
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ awaitAtMostTenSeconds.untilAsserted { () =>
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState()}",
+ | "canCalculateChanges": false,
+ | "position": 0,
+ | "limit": 256,
+ | "ids": []
+ | },
+ | "c1"
+ | ]]
+ |}""".stripMargin)
+ }
+ }
+
+ @Test
+ def emailInSharedMailboxesShouldBeDisplayedWhenExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail",
+ | "urn:apache:james:params:jmap:mail:shares"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6"
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ awaitAtMostTenSeconds.untilAsserted { () =>
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState(messageId1)}",
+ | "canCalculateChanges": false,
+ | "position": 0,
+ | "limit": 256,
+ | "ids": ["${messageId1.serialize()}"]
+ | },
+ | "c1"
+ | ]]
+ |}""".stripMargin)
+ }
+ }
+
+ @Test
+ def inMailboxFilterShouldReturnEmptyForSharedMailboxesWhenNoExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter": {"inMailbox": "${andreInboxId.serialize()}"}
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | [
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState()}",
+ | "canCalculateChanges": false,
+ | "ids": [
+ |
+ | ],
+ | "position": 0,
+ | "limit": 256
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def inMailboxFilterShouldAcceptSharedMailboxesWhenExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail",
+ | "urn:apache:james:params:jmap:mail:shares"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter": {"inMailbox": "${andreInboxId.serialize()}"}
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ awaitAtMostTenSeconds.untilAsserted { () =>
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState(messageId1)}",
+ | "canCalculateChanges": false,
+ | "position": 0,
+ | "limit": 256,
+ | "ids": ["${messageId1.serialize()}"]
+ | },
+ | "c1"
+ | ]]
+ |}""".stripMargin)
+ }
+ }
+
+ @Test
+ def inMailboxOtherThanFilterShouldReturnEmptyForSharedMailboxesWhenNoExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter": {"inMailboxOtherThan": ["${andreInboxId.serialize()}"]}
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState()}",
+ | "canCalculateChanges": false,
+ | "ids": [],
+ | "position": 0,
+ | "limit": 256
+ | },
+ | "c1"
+ | ]]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def inMailboxOtherThanFilterShouldAcceptSharedMailboxesWhenExtension(server: GuiceJamesServer): Unit = {
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
+ val messageId1: MessageId = mailboxProbe
+ .appendMessage(ANDRE.asString, inbox(ANDRE),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+ val messageId2: MessageId = mailboxProbe
+ .appendMessage(BOB.asString, inbox(BOB),
+ AppendCommand.from(
+ Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build))
+ .getMessageId
+
+ server.getProbe(classOf[ACLProbeImpl])
+ .replaceRights(inbox(ANDRE), BOB.asString, new MailboxACL.Rfc4314Rights(Right.Read))
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail",
+ | "urn:apache:james:params:jmap:mail:shares"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter": {"inMailboxOtherThan": ["${andreInboxId.serialize()}"]}
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ awaitAtMostTenSeconds.untilAsserted { () =>
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "queryState": "${generateQueryState(messageId2)}",
+ | "canCalculateChanges": false,
+ | "position": 0,
+ | "limit": 256,
+ | "ids": ["${messageId2.serialize()}"]
+ | },
+ | "c1"
+ | ]]
+ |}""".stripMargin)
+ }
+ }
+
+ @Test
def hasAttachmentShouldKeepMessageWithoutAttachmentWhenFalse(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
@@ -1342,7 +1732,7 @@ trait EmailQueryMethodContract {
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.body(request)
.when
- .post.prettyPeek()
+ .post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
@@ -1391,7 +1781,7 @@ trait EmailQueryMethodContract {
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.body(request)
.when
- .post.prettyPeek()
+ .post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 6bee1dc..4feb97e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -45,13 +45,13 @@ import scala.jdk.CollectionConverters._
class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
mailboxManager: MailboxManager,
metricFactory: MetricFactory) extends Method {
- override val methodName = MethodName("Email/query")
+ override val methodName: MethodName = MethodName("Email/query")
override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] =
metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
asEmailQueryRequest(invocation.arguments)
- .flatMap(processRequest(mailboxSession, invocation, _))
+ .flatMap(processRequest(mailboxSession, invocation, _, capabilities))
.onErrorResume {
case e: UnsupportedRequestParameterException => SMono.just(Invocation.error(
ErrorCode.InvalidArguments,
@@ -71,10 +71,10 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
}
.map(invocationResult => (invocationResult, processingContext)))
- private def processRequest(mailboxSession: MailboxSession, invocation: Invocation, request: EmailQueryRequest): SMono[Invocation] = {
- searchQueryFromRequest(request) match {
+ private def processRequest(mailboxSession: MailboxSession, invocation: Invocation, request: EmailQueryRequest, capabilities: Set[CapabilityIdentifier]): SMono[Invocation] = {
+ searchQueryFromRequest(request, capabilities, mailboxSession) match {
case Left(error) => SMono.raiseError(error)
- case Right(searchQuery) => for {
+ case Right(searchQuery) => for {
positionToUse <- Position.validateRequestPosition(request.position)
limitToUse <- Limit.validateRequestLimit(request.limit)
response <- executeQuery(mailboxSession, request, searchQuery, positionToUse, limitToUse)
@@ -94,7 +94,7 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
limit = Some(limitToUse).filterNot(used => request.limit.map(_.value).contains(used.value))))
}
- private def searchQueryFromRequest(request: EmailQueryRequest): Either[UnsupportedOperationException, MultimailboxesSearchQuery] = {
+ private def searchQueryFromRequest(request: EmailQueryRequest, capabilities: Set[CapabilityIdentifier], session: MailboxSession): Either[UnsupportedOperationException, MultimailboxesSearchQuery] = {
val comparators: List[Comparator] = request.comparator.getOrElse(Set(Comparator.default)).toList
comparators.map(_.toSort)
@@ -104,7 +104,7 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
} yield queryFilter
.sorts(sorts.asJava)
.build())
- .map(MailboxFilter.buildQuery(request, _))
+ .map(MailboxFilter.buildQuery(request, _, capabilities, session))
}
private def asEmailQueryRequest(arguments: Arguments): SMono[EmailQueryRequest] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index 3ef1239..c547cf2 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -22,6 +22,10 @@ import java.util.Date
import cats.implicits._
import org.apache.james.jmap.mail.{EmailQueryRequest, UnsupportedFilterException}
+import org.apache.james.jmap.model.CapabilityIdentifier
+import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery.{AccessibleNamespace, Namespace, PersonalNamespace}
import org.apache.james.mailbox.model.SearchQuery.DateResolution.Second
import org.apache.james.mailbox.model.SearchQuery.{DateComparator, DateOperator, DateResolution, InternalDateCriterion}
import org.apache.james.mailbox.model.{MultimailboxesSearchQuery, SearchQuery}
@@ -47,13 +51,20 @@ case object NotInMailboxFilter extends MailboxFilter {
}
object MailboxFilter {
- def buildQuery(request: EmailQueryRequest, searchQuery: SearchQuery) = {
+ def buildQuery(request: EmailQueryRequest, searchQuery: SearchQuery, capabilities: Set[CapabilityIdentifier], session: MailboxSession) = {
val multiMailboxQueryBuilder = MultimailboxesSearchQuery.from(searchQuery)
+ .inNamespace(queryNamespace(capabilities, session))
List(InMailboxFilter, NotInMailboxFilter).foldLeft(multiMailboxQueryBuilder)((builder, filter) => filter.toQuery(builder, request))
.build()
}
+ private def queryNamespace(capabilities: Set[CapabilityIdentifier], session: MailboxSession): Namespace = if (capabilities.contains(CapabilityIdentifier.JAMES_SHARES)) {
+ new AccessibleNamespace()
+ } else {
+ new PersonalNamespace(session)
+ }
+
sealed trait QueryFilter {
def toQuery(builder: SearchQuery.Builder, request: EmailQueryRequest): Either[UnsupportedFilterException, SearchQuery.Builder]
}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 05/13: JAMES-3379 Remove unused Option
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 0e0b6437522bea94ec39803460a8cee9780ecf91
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Thu Sep 24 12:02:50 2020 +0700
JAMES-3379 Remove unused Option
---
.../scala/org/apache/james/jmap/mail/Email.scala | 18 +++++++-------
.../org/apache/james/jmap/mail/EmailGet.scala | 2 +-
.../org/apache/james/jmap/mail/EmailHeader.scala | 28 +++++++++++-----------
3 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 0653d80..8efb10f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -182,30 +182,30 @@ object ParseOptions {
}
sealed trait ParseOption {
- def extractHeaderValue(field: Field): Option[EmailHeaderValue]
+ def extractHeaderValue(field: Field): EmailHeaderValue
}
case object AsRaw extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(RawHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = RawHeaderValue.from(field)
}
case object AsText extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(TextHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = TextHeaderValue.from(field)
}
case object AsAddresses extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(AddressesHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = AddressesHeaderValue.from(field)
}
case object AsGroupedAddresses extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(GroupedAddressesHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = GroupedAddressesHeaderValue.from(field)
}
case object AsMessageIds extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(MessageIdsHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = MessageIdsHeaderValue.from(field)
}
case object AsDate extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, ZoneId.systemDefault()))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = DateHeaderValue.from(field, ZoneId.systemDefault())
- def extractHeaderValue(field: Field, zoneId: ZoneId): Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, zoneId))
+ def extractHeaderValue(field: Field, zoneId: ZoneId): EmailHeaderValue = DateHeaderValue.from(field, zoneId)
}
case object AsURLs extends ParseOption {
- override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(URLsHeaderValue.from(field))
+ override def extractHeaderValue(field: Field): EmailHeaderValue = URLsHeaderValue.from(field)
}
case class HeaderMessageId(value: String) extends AnyVal
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index ea49946..cd4ae81 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -91,7 +91,7 @@ case class SpecificHeaderRequest(headerName: NonEmptyString, property: String, p
.map(_.asScala)
.flatMap(fields => fields.reverse.headOption)
- (headerName, field.flatMap({
+ (headerName, field.map({
val option = parseOption.getOrElse(AsRaw)
option match {
case AsDate => AsDate.extractHeaderValue(_, zoneId)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 7f05aec..8c8247c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -56,23 +56,23 @@ object GroupedAddressesHeaderValue extends EmailHeaderValue {
.asScala
.toList
- val groups: List[EmailAddressGroup] = addresses
- .flatMap({
- case group: Group => Some(group)
- case _ => None
- })
- .map(group => EmailAddressGroup(Some(GroupName(group.getName)), EmailAddress.from(group.getMailboxes)))
-
- val addressesWithoutGroup: List[EmailAddress] = addresses
- .flatMap({
- case mailbox: Mime4jMailbox => Some(mailbox)
- case _ => None
- })
- .map(EmailAddress.from(_))
-
if (addresses.isEmpty) {
GroupedAddressesHeaderValue(List())
} else {
+ val groups: List[EmailAddressGroup] = addresses
+ .flatMap({
+ case group: Group => Some(group)
+ case _ => None
+ })
+ .map(group => EmailAddressGroup(Some(GroupName(group.getName)), EmailAddress.from(group.getMailboxes)))
+
+ val addressesWithoutGroup: List[EmailAddress] = addresses
+ .flatMap({
+ case mailbox: Mime4jMailbox => Some(mailbox)
+ case _ => None
+ })
+ .map(EmailAddress.from(_))
+
GroupedAddressesHeaderValue(List(EmailAddressGroup(None, addressesWithoutGroup)) ++ groups)
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 10/13: JAMES-3359 Reject Mailbox/set parentId
update to self
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 03a8293c0cb5610dedd6d4d913854306af6976c8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Sep 23 14:54:00 2020 +0700
JAMES-3359 Reject Mailbox/set parentId update to self
The spec states:
Mailboxes form acyclic graphs (forests) directed by the child-to-parent relationship. There MUST NOT be a loop.
```
```
Rejecting self is enough as parentId property cannot be updated if the mailbox contains child mailboxes
---
.../contract/MailboxSetMethodContract.scala | 128 +++++++++++++++++++++
.../james/jmap/method/MailboxSetMethod.scala | 5 +
2 files changed, 133 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/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 cbe2daa..42a26f8 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
@@ -5600,6 +5600,134 @@ trait MailboxSetMethodContract {
}
@Test
+ def parentIdUpdateSetToSelfShouldBeRejected(server: GuiceJamesServer): Unit = {
+ val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ .createMailbox(MailboxPath.forUser(BOB, "mailbox"))
+ val request =
+ s"""
+ |{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "update": {
+ | "${mailboxId.serialize()}": {
+ | "parentId": "${mailboxId.serialize()}"
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}
+ |""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .log().ifValidationFails()
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "notUpdated": {
+ | "${mailboxId.serialize()}": {
+ | "type": "invalidArguments",
+ | "description": "A mailbox parentId property can not be set to itself or one of its child",
+ | "properties": [
+ | "parentId"
+ | ]
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def parentIdUpdateSetToAChildShouldBeRejected(server: GuiceJamesServer): Unit = {
+ val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ .createMailbox(MailboxPath.forUser(BOB, "mailbox"))
+ val childId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ .createMailbox(MailboxPath.forUser(BOB, "mailbox.child"))
+ val request =
+ s"""
+ |{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "update": {
+ | "${mailboxId.serialize()}": {
+ | "parentId": "${childId.serialize()}"
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}
+ |""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .log().ifValidationFails()
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "notUpdated": {
+ | "${mailboxId.serialize()}": {
+ | "type": "invalidArguments",
+ | "description": "${mailboxId.serialize()} parentId property cannot be updated as this mailbox has child mailboxes",
+ | "properties": [
+ | "parentId"
+ | ]
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
def updateShouldAllowParentIdChangeWhenChildMailbox(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(MailboxPath.forUser(BOB, "parent"))
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 514d8d2..465b22c 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
@@ -45,6 +45,7 @@ import scala.jdk.CollectionConverters._
case class MailboxHasMailException(mailboxId: MailboxId) extends Exception
case class SystemMailboxChangeException(mailboxId: MailboxId) extends Exception
+case class LoopInMailboxGraphException(mailboxId: MailboxId) extends Exception
case class MailboxHasChildException(mailboxId: MailboxId) extends Exception
case class MailboxCreationParseException(setError: SetError) extends Exception
@@ -123,6 +124,7 @@ case class UpdateFailure(mailboxId: UnparsedMailboxId, exception: Throwable, pat
case e: InvalidPropertyException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
case e: InvalidPatchException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("Invalid change to a system mailbox"), filter(Properties("name", "parentId")))
+ case e: LoopInMailboxGraphException => SetError.invalidArguments(SetErrorDescription("A mailbox parentId property can not be set to itself or one of its child"), Some(Properties("parentId")))
case e: InsufficientRightsException => SetError.invalidArguments(SetErrorDescription("Invalid change to a delegated mailbox"))
case e: MailboxHasChildException => SetError.invalidArguments(SetErrorDescription(s"${e.mailboxId.serialize()} parentId property cannot be updated as this mailbox has child mailboxes"), Some(Properties("parentId")))
case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
@@ -219,6 +221,9 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
if (isASystemMailbox(mailbox)) {
throw SystemMailboxChangeException(mailboxId)
}
+ if (validatedPatch.parentIdUpdate.flatMap(_.newId).contains(mailboxId)) {
+ throw LoopInMailboxGraphException(mailboxId)
+ }
val oldPath = mailbox.getMailboxPath
val newPath = applyParentIdUpdate(mailboxId, validatedPatch.parentIdUpdate, mailboxSession)
.andThen(applyNameUpdate(validatedPatch.nameUpdate, mailboxSession))
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 02/13: JAMES-3379 Email/get specific parsed
headers: asMessageIds
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 d5717e42cb12fd249eeaf5743b67fc5e3c5f798a
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Tue Sep 22 15:30:31 2020 +0700
JAMES-3379 Email/get specific parsed headers: asMessageIds
---
.../rfc8621/contract/EmailGetMethodContract.scala | 224 ++++++++++++++++++++-
.../james/jmap/json/EmailGetSerializer.scala | 4 +-
.../scala/org/apache/james/jmap/mail/Email.scala | 25 ++-
.../org/apache/james/jmap/mail/EmailHeader.scala | 22 +-
4 files changed, 260 insertions(+), 15 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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index 601ba30..f266760 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -2105,7 +2105,8 @@ trait EmailGetMethodContract {
.inPath("methodResponses[0][1].list[0]")
.isEqualTo(
s"""{
- | "id": "${messageId.serialize}"
+ | "id": "${messageId.serialize}",
+ | "messageId":null
|}""".stripMargin)
}
@@ -2148,7 +2149,8 @@ trait EmailGetMethodContract {
.inPath("methodResponses[0][1].list[0]")
.isEqualTo(
s"""{
- | "id": "${messageId.serialize}"
+ | "id": "${messageId.serialize}",
+ | "inReplyTo":null
|}""".stripMargin)
}
@@ -2191,7 +2193,8 @@ trait EmailGetMethodContract {
.inPath("methodResponses[0][1].list[0]")
.isEqualTo(
s"""{
- | "id": "${messageId.serialize}"
+ | "id": "${messageId.serialize}",
+ | "references":null
|}""".stripMargin)
}
@@ -6197,4 +6200,219 @@ trait EmailGetMethodContract {
| "header:To:asGroupedAddresses": []
}""".stripMargin)
}
+
+ @Test
+ def emailGetShouldReturnSpecificHeaderAsMessageIds(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .setMessageId("<12...@local.machine.example>")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:Message-Id:asMessageIds"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:Message-Id:asMessageIds": [
+ | "1234@local.machine.example"
+ | ]
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetSpecificHeaderAsMessageIdsShouldSupportMultipleIds(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("References", "<12...@local.machine.example> \r\n <34...@example.net>"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:References:asMessageIds"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:References:asMessageIds": [
+ | "1234@local.machine.example",
+ | "3456@example.net"
+ | ]
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetAsMessageIdsHeaderShouldReturnNullWhenInvalidMessageIds(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .setMessageId("invalid")
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:Message-Id:asMessageIds"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:Message-Id:asMessageIds": null
+ }""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnSpecificHeaderWhenPartialInvalidMessageIds(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("References", "invalid bloblah \r\n <34...@example.net>"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:References:asMessageIds"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:References:asMessageIds": [
+ | "3456@example.net"
+ | ]
+ }""".stripMargin)
+ }
}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index ae33dc1..f937c44 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
package org.apache.james.jmap.json
import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, GroupName, Gr [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, GroupName, Gr [...]
import org.apache.james.jmap.model._
import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
import play.api.libs.functional.syntax._
@@ -63,11 +63,13 @@ object EmailGetSerializer {
"name" -> Json.toJson(o.name),
"addresses" -> Json.toJson(o.addresses))
private implicit val groupedAddressesHeaderWrites: Writes[GroupedAddressesHeaderValue] = Json.valueWrites[GroupedAddressesHeaderValue]
+ private implicit val messageIdsHeaderWrites: Writes[MessageIdsHeaderValue] = Json.valueWrites[MessageIdsHeaderValue]
private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
case headerValue: RawHeaderValue => Json.toJson[RawHeaderValue](headerValue)
case headerValue: TextHeaderValue => Json.toJson[TextHeaderValue](headerValue)
case headerValue: AddressesHeaderValue => Json.toJson[AddressesHeaderValue](headerValue)
case headerValue: GroupedAddressesHeaderValue => Json.toJson[GroupedAddressesHeaderValue](headerValue)
+ case headerValue: MessageIdsHeaderValue => Json.toJson[MessageIdsHeaderValue](headerValue)
}
private implicit val headersWrites: Writes[EmailHeader] = Json.writes[EmailHeader]
private implicit val bodyValueWrites: Writes[EmailBodyValue] = Json.writes[EmailBodyValue]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index ece0fe3..de92da1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -167,6 +167,7 @@ object ParseOptions {
case "asText" => Some(AsText)
case "asAddresses" => Some(AsAddresses)
case "asGroupedAddresses" => Some(AsGroupedAddresses)
+ case "asMessageIds" => Some(AsMessageIds)
case _ => None
}
}
@@ -186,6 +187,9 @@ case object AsAddresses extends ParseOption {
case object AsGroupedAddresses extends ParseOption {
override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(GroupedAddressesHeaderValue.from(field))
}
+case object AsMessageIds extends ParseOption {
+ override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(MessageIdsHeaderValue.from(field))
+}
case class HeaderMessageId(value: String) extends AnyVal
@@ -246,13 +250,14 @@ object EmailHeaders {
.map(MimeUtil.unscrambleHeaderValue)
.map(Subject)
- private def extractMessageId(mime4JMessage: Message, fieldName: String): Option[List[HeaderMessageId]] =
- Option(mime4JMessage.getHeader.getFields(fieldName))
- .map(_.asScala
- .map(_.getBody)
- .map(HeaderMessageId.from)
- .toList)
- .filter(_.nonEmpty)
+ private def extractMessageId(mime4JMessage: Message, fieldName: String): MessageIdsHeaderValue =
+ MessageIdsHeaderValue(
+ Option(mime4JMessage.getHeader.getFields(fieldName))
+ .map(_.asScala
+ .map(_.getBody)
+ .map(HeaderMessageId.from)
+ .toList)
+ .filter(_.nonEmpty))
private def extractAddresses(mime4JMessage: Message, fieldName: String): Option[AddressesHeaderValue] =
extractLastField(mime4JMessage, fieldName)
@@ -279,9 +284,9 @@ object EmailHeaders {
}
case class EmailHeaders(headers: List[EmailHeader],
- messageId: Option[List[HeaderMessageId]],
- inReplyTo: Option[List[HeaderMessageId]],
- references: Option[List[HeaderMessageId]],
+ messageId: MessageIdsHeaderValue,
+ inReplyTo: MessageIdsHeaderValue,
+ references: MessageIdsHeaderValue,
to: Option[AddressesHeaderValue],
cc: Option[AddressesHeaderValue],
bcc: Option[AddressesHeaderValue],
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index e1da31b..58db534 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -20,10 +20,12 @@
package org.apache.james.jmap.mail
import java.nio.charset.StandardCharsets.US_ASCII
+import java.util.Date
+import org.apache.commons.lang3.StringUtils
import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
-import org.apache.james.mime4j.field.AddressListFieldImpl
+import org.apache.james.mime4j.field.{AddressListFieldImpl, DateTimeFieldImpl}
import org.apache.james.mime4j.stream.Field
import org.apache.james.mime4j.util.MimeUtil
@@ -75,6 +77,23 @@ object GroupedAddressesHeaderValue extends EmailHeaderValue {
}
}
+object MessageIdsHeaderValue {
+ def from(field: Field): MessageIdsHeaderValue = {
+ val messageIds: List[HeaderMessageId] = MimeUtil.unfold(StringUtils.normalizeSpace(field.getBody))
+ .split(' ')
+ .flatMap(body => {
+ if(body.startsWith("<") && body.endsWith(">") && body.contains("@")) {
+ scala.Right(HeaderMessageId.from(body))
+ } else {
+ Left()
+ }
+ }.toOption)
+ .toList
+
+ MessageIdsHeaderValue(Option(messageIds).filter(_.nonEmpty))
+ }
+}
+
case class EmailHeaderName(value: String) extends AnyVal
sealed trait EmailHeaderValue
@@ -82,5 +101,6 @@ case class RawHeaderValue(value: String) extends EmailHeaderValue
case class TextHeaderValue(value: String) extends EmailHeaderValue
case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderValue
case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue
+case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends EmailHeaderValue
case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 07/13: JAMES-3305 MailQueue creation should not
fail when no dead-letter set up
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 c342b13ff2acfd72472caee66d4164717fc02115
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Sep 22 12:21:34 2020 +0700
JAMES-3305 MailQueue creation should not fail when no dead-letter set up
---
.../rabbitmq/RabbitMqMailQueueFactoryTest.java | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMqMailQueueFactoryTest.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMqMailQueueFactoryTest.java
index ecf5c1c..a028e99 100644
--- a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMqMailQueueFactoryTest.java
+++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/RabbitMqMailQueueFactoryTest.java
@@ -19,6 +19,10 @@
package org.apache.james.queue.rabbitmq;
+import static org.apache.james.backends.rabbitmq.Constants.AUTO_DELETE;
+import static org.apache.james.backends.rabbitmq.Constants.DURABLE;
+import static org.apache.james.backends.rabbitmq.Constants.EXCLUSIVE;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -38,8 +42,11 @@ import org.apache.james.queue.rabbitmq.view.api.MailQueueView;
import org.apache.james.queue.rabbitmq.view.cassandra.CassandraMailQueueBrowser;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import reactor.rabbitmq.QueueSpecification;
+
class RabbitMqMailQueueFactoryTest implements MailQueueFactoryContract<RabbitMQMailQueue> {
private static final HashBlobId.Factory BLOB_ID_FACTORY = new HashBlobId.Factory();
@@ -87,4 +94,19 @@ class RabbitMqMailQueueFactoryTest implements MailQueueFactoryContract<RabbitMQM
return mailQueueFactory;
}
+ @Test
+ void getQueueShouldNotFailWhenTheQueueExistsWithoutDeadLetterSetUp() {
+ rabbitMQExtension.getSender()
+ .declareQueue(QueueSpecification.queue("JamesMailQueue-workqueue-" + NAME_1.asString())
+ .durable(DURABLE)
+ .exclusive(!EXCLUSIVE)
+ .autoDelete(!AUTO_DELETE))
+ .block();
+
+ mailQueueFactory.createQueue(NAME_1);
+
+ assertThatCode(() -> mailQueueFactory.getQueue(NAME_1))
+ .doesNotThrowAnyException();
+ }
+
}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 11/13: JAMES-3359 Remove needless brackets
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 662604a9f11f49fb90da82617c044dcfbe9dcfec
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Sep 24 16:39:01 2020 +0700
JAMES-3359 Remove needless brackets
---
.../contract/MailboxSetMethodContract.scala | 500 ++++++++++-----------
1 file changed, 250 insertions(+), 250 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 42a26f8..d87adb0 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
@@ -74,7 +74,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "role": "Draft"
| }
| }
@@ -104,7 +104,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["role"]
@@ -128,7 +128,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sortOrder": 42
| }
| }
@@ -158,7 +158,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["sortOrder"]
@@ -182,7 +182,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "quotas": {}
| }
| }
@@ -212,7 +212,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["quotas"]
@@ -236,7 +236,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "namespace": "Personal"
| }
| }
@@ -266,7 +266,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["namespace"]
@@ -290,7 +290,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "myRights": {}
| }
| }
@@ -320,7 +320,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["myRights"]
@@ -399,7 +399,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "unreadThreads": 42
| }
| }
@@ -429,7 +429,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["unreadThreads"]
@@ -453,7 +453,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "totalThreads": 42
| }
| }
@@ -483,7 +483,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["totalThreads"]
@@ -507,7 +507,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "unreadEmails": 42
| }
| }
@@ -537,7 +537,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["unreadEmails"]
@@ -561,7 +561,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "totalEmails": 42
| }
| }
@@ -591,7 +591,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Can not modify server-set properties",
| "properties":["totalEmails"]
@@ -951,7 +951,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1116,7 +1116,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1323,7 +1323,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1453,7 +1453,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1521,7 +1521,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1593,7 +1593,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -1718,7 +1718,7 @@ trait MailboxSetMethodContract {
| "notCreated": {
| "C42": {
| "type": "invalidArguments",
- | "description": "${mailboxId.serialize()} can not be found",
+ | "description": "${mailboxId.serialize} can not be found",
| "properties":["parentId"]
| }
| }
@@ -1993,7 +1993,7 @@ trait MailboxSetMethodContract {
| "Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c1"]]
|}""".stripMargin)
@@ -2017,7 +2017,7 @@ trait MailboxSetMethodContract {
|
| ],
| "notFound": [
- | "${mailboxId.serialize()}"
+ | "${mailboxId.serialize}"
| ]
| },
| "c1"
@@ -2069,7 +2069,7 @@ trait MailboxSetMethodContract {
| "Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId1.serialize()}", "${mailboxId2.serialize()}"]
+ | "ids": ["${mailboxId1.serialize}", "${mailboxId2.serialize}"]
| },
| "c1"]]
|}""".stripMargin)
@@ -2095,7 +2095,7 @@ trait MailboxSetMethodContract {
|
| ],
| "notFound": [
- | "${mailboxId1.serialize()}", "${mailboxId2.serialize()}"
+ | "${mailboxId1.serialize}", "${mailboxId2.serialize}"
| ]
| },
| "c1"
@@ -2116,7 +2116,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"]
+ | "destroy": ["${mailboxId.serialize}"]
| },
| "c1"
| ]
@@ -2146,9 +2146,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
- | "description": "${mailboxId.serialize()} can not be found"
+ | "description": "${mailboxId.serialize} can not be found"
| }
| }
| },
@@ -2177,7 +2177,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"]
+ | "destroy": ["${mailboxId.serialize}"]
| },
| "c1"
| ]
@@ -2207,9 +2207,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "mailboxHasEmail",
- | "description": "${mailboxId.serialize()} is not empty"
+ | "description": "${mailboxId.serialize} is not empty"
| }
| }
| },
@@ -2231,7 +2231,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"]
+ | "destroy": ["${mailboxId.serialize}"]
| },
| "c1"
| ]
@@ -2261,9 +2261,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "mailboxHasChild",
- | "description": "${mailboxId.serialize()} has child mailboxes"
+ | "description": "${mailboxId.serialize} has child mailboxes"
| }
| }
| },
@@ -2287,7 +2287,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"]
+ | "destroy": ["${mailboxId.serialize}"]
| },
| "c1"
| ]
@@ -2317,7 +2317,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
| "description": "#private:andre@domain.tld:mailbox"
| }
@@ -2466,10 +2466,10 @@ trait MailboxSetMethodContract {
val parentId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "parent")
- .serialize()
+ .serialize
val childId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "parent.child")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -2581,7 +2581,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "myMailbox")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -2738,7 +2738,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "name": "newName"
| }
| }
@@ -2748,7 +2748,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "name"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -2776,14 +2776,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "newName"
| }],
| "notFound": []
@@ -2838,7 +2838,7 @@ trait MailboxSetMethodContract {
val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "newName")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -2936,7 +2936,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": "newName"
| }
| }
@@ -2966,7 +2966,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "Mailbox with name=#private:bob@domain.tld:newName already exists.",
| "properties": ["name"]
@@ -2989,7 +2989,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": ["newName"]
| }
| }
@@ -3019,7 +3019,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "Expecting a JSON string as an argument",
| "properties": ["name"]
@@ -3042,7 +3042,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "unknown": "newValue"
| }
| }
@@ -3072,7 +3072,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "unknown property do not exist thus cannot be updated",
| "properties": ["unknown"]
@@ -3095,7 +3095,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "/name": "newValue"
| }
| }
@@ -3125,7 +3125,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "/name property do not exist thus cannot be updated",
| "properties": ["/name"]
@@ -3148,7 +3148,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "": "newValue"
| }
| }
@@ -3179,7 +3179,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidPatch",
| "description": "$message"
| }
@@ -3201,7 +3201,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": "${"a".repeat(201)}"
| }
| }
@@ -3231,7 +3231,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "Mailbox name exceeds maximum size of 200 characters",
| "properties": ["name"]
@@ -3254,7 +3254,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": "newName"
| }
| }
@@ -3284,9 +3284,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "notFound",
- | "description": "${mailboxId1.serialize()} can not be found"
+ | "description": "${mailboxId1.serialize} can not be found"
| }
| }
| }, "c1"]
@@ -3309,7 +3309,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": "newName"
| }
| }
@@ -3339,7 +3339,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "notFound",
| "description": "#private:andre@domain.tld:previousName"
| }
@@ -3362,7 +3362,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "name": "newName"
| }
| }
@@ -3392,7 +3392,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Invalid change to a system mailbox",
| "properties":["name"]
@@ -3415,7 +3415,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"]
+ | "destroy": ["${mailboxId.serialize}"]
| },
| "c1"]]
|}
@@ -3442,7 +3442,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "System mailboxes cannot be destroyed"
| }
@@ -3468,7 +3468,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${childId.serialize()}": {
+ | "${childId.serialize}": {
| "name": "newChild"
| }
| }
@@ -3479,7 +3479,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "name", "parentId"],
- | "ids": ["${childId.serialize()}"]
+ | "ids": ["${childId.serialize}"]
| },
| "c2"]
| ]
@@ -3507,16 +3507,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${childId.serialize()}": {}
+ | "${childId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${childId.serialize()}",
+ | "id": "${childId.serialize}",
| "name": "newChild",
- | "parentId": "${parentId.serialize()}"
+ | "parentId": "${parentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -3540,7 +3540,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${parentId.serialize()}": {
+ | "${parentId.serialize}": {
| "name": "newParent"
| }
| }
@@ -3551,7 +3551,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "name", "parentId"],
- | "ids": ["${childId.serialize()}"]
+ | "ids": ["${childId.serialize}"]
| },
| "c2"]
| ]
@@ -3579,16 +3579,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${parentId.serialize()}": {}
+ | "${parentId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${childId.serialize()}",
+ | "id": "${childId.serialize}",
| "name": "child",
- | "parentId": "${parentId.serialize()}"
+ | "parentId": "${parentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -3702,7 +3702,7 @@ trait MailboxSetMethodContract {
| "Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c1"]]
|}""".stripMargin)
@@ -3726,7 +3726,7 @@ trait MailboxSetMethodContract {
|
| ],
| "notFound": [
- | "${mailboxId.serialize()}"
+ | "${mailboxId.serialize}"
| ]
| },
| "c1"
@@ -3811,7 +3811,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"],
+ | "destroy": ["${mailboxId.serialize}"],
| "onDestroyRemoveEmails": false
| },
| "c1"
@@ -3842,9 +3842,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notDestroyed": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "mailboxHasEmail",
- | "description": "${mailboxId.serialize()} is not empty"
+ | "description": "${mailboxId.serialize} is not empty"
| }
| }
| },
@@ -3866,7 +3866,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -3876,7 +3876,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -3904,14 +3904,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": true
| }],
| "notFound": []
@@ -3933,7 +3933,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -3943,7 +3943,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": false
| }
| }
@@ -3953,7 +3953,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c4"]
| ]
@@ -3981,21 +3981,21 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/set", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c3"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": false
| }],
| "notFound": []
@@ -4017,7 +4017,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": null
| }
| }
@@ -4027,7 +4027,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c4"]
| ]
@@ -4055,14 +4055,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c3"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": true
| }],
| "notFound": []
@@ -4089,7 +4089,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -4099,7 +4099,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed", "namespace"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c4"]
| ]
@@ -4127,14 +4127,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c3"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": true,
| "namespace": "Delegated[andre@domain.tld]"
| }],
@@ -4161,7 +4161,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -4171,7 +4171,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": false
| }
| }
@@ -4181,7 +4181,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed", "namespace"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c4"]
| ]
@@ -4209,21 +4209,21 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/set", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c3"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": false,
| "namespace": "Delegated[andre@domain.tld]"
| }],
@@ -4245,7 +4245,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "name": "a.b"
| }
| }
@@ -4275,7 +4275,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId1.serialize()}": {
+ | "${mailboxId1.serialize}": {
| "type": "invalidArguments",
| "description": "The mailbox 'a.b' contains an illegal character: '.'",
| "properties": ["name"]
@@ -4304,10 +4304,10 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${andreId.serialize()}" : {
+ | "${andreId.serialize}" : {
| "isSubscribed": true
| },
- | "${bobId.serialize()}" : {
+ | "${bobId.serialize}" : {
| "isSubscribed": false
| }
| }
@@ -4317,7 +4317,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed"],
- | "ids": ["${andreId.serialize()}", "${bobId.serialize()}"]
+ | "ids": ["${andreId.serialize}", "${bobId.serialize}"]
| },
| "c4"]
| ]
@@ -4345,18 +4345,18 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${andreId.serialize()}": {},
- | "${bobId.serialize()}": {}
+ | "${andreId.serialize}": {},
+ | "${bobId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${andreId.serialize()}",
+ | "id": "${andreId.serialize}",
| "isSubscribed": true
| }, {
- | "id": "${bobId.serialize()}",
+ | "id": "${bobId.serialize}",
| "isSubscribed": false
| }],
| "notFound": []
@@ -4382,7 +4382,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -4392,7 +4392,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": null
| }
| }
@@ -4402,7 +4402,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "isSubscribed", "namespace"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c4"]
| ]
@@ -4430,21 +4430,21 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/set", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c3"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "isSubscribed": false,
| "namespace": "Delegated[andre@domain.tld]"
| }],
@@ -4472,7 +4472,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": true
| }
| }
@@ -4508,7 +4508,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}" : {
+ | "${mailboxId.serialize}" : {
| "isSubscribed": "invalid"
| }
| }
@@ -4539,7 +4539,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Expecting a JSON boolean as an argument",
| "properties": ["isSubscribed"]
@@ -4571,7 +4571,7 @@ trait MailboxSetMethodContract {
| "Mailbox/set",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "destroy": ["${mailboxId.serialize()}"],
+ | "destroy": ["${mailboxId.serialize}"],
| "onDestroyRemoveEmails": false
| },
| "c1"
@@ -4607,7 +4607,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${ANDRE.asString()}":["r", "l"]
| }
@@ -4669,7 +4669,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/${ANDRE.asString()}": ["r", "l"]
| }
| }
@@ -4729,7 +4729,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "/quotas": "toto"
| }
| }
@@ -4790,7 +4790,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${ANDRE.asString()}":["r", "l"]
| }
@@ -4803,7 +4803,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "rights"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -4831,14 +4831,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "rights": {
| "${ANDRE.asString()}": ["l", "r"]
| }
@@ -4870,7 +4870,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${ANDRE.asString()}":["r", "l"],
| "${DAVID.asString()}":["r", "l", "w"]
@@ -4884,7 +4884,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "rights"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -4912,14 +4912,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "rights": {
| "${ANDRE.asString()}": ["l", "r"],
| "${DAVID.asString()}": ["l", "r", "w"]
@@ -4949,7 +4949,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${DAVID.asString()}":["r", "l", "w"],
| "${BOB.asString()}":["r", "l", "w", "a"]
@@ -4963,7 +4963,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "namespace", "rights"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -4991,7 +4991,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Invalid change to a delegated mailbox"
| }
@@ -5001,7 +5001,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "namespace": "Delegated[andre@domain.tld]",
| "rights": {
| "bob@domain.tld": ["l"]
@@ -5032,7 +5032,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${DAVID.asString()}":["r", "l", "w"],
| "${BOB.asString()}":["r", "l", "w", "a"]
@@ -5067,9 +5067,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
- | "description": "${mailboxId.serialize()} can not be found"
+ | "description": "${mailboxId.serialize} can not be found"
| }
| }
| }, "c1"]
@@ -5095,7 +5095,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": "invalid"
| }
| }
@@ -5127,7 +5127,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Specified value do not match the expected JSON format: List((,List(JsonValidationError(List(error.expected.jsobject),ArraySeq()))))",
| "properties": ["sharedWith"]
@@ -5156,7 +5156,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/${ANDRE.asString()}": ["r", "l"]
| }
| }
@@ -5167,7 +5167,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "properties": ["id", "rights"],
- | "ids": ["${mailboxId.serialize()}"]
+ | "ids": ["${mailboxId.serialize}"]
| },
| "c2"]
| ]
@@ -5195,14 +5195,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "rights": {
| "${ANDRE.asString()}": ["l", "r"],
| "${DAVID.asString()}": ["l"]
@@ -5229,7 +5229,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/invalid@invalid@${DOMAIN.asString()}": ["r", "l"]
| }
| }
@@ -5261,7 +5261,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidPatch",
| "description": "The username should not contain multiple domain delimiter. Value: invalid@invalid@domain.tld"
| }
@@ -5286,7 +5286,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/${ANDRE.asString()}": ["p"]
| }
| }
@@ -5320,7 +5320,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Specified value do not match the expected JSON format: List(((0),List(JsonValidationError(List(Unknown right 'p'),ArraySeq()))))",
| "properties": [
@@ -5350,7 +5350,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith": {
| "${DAVID.asString()}":["r", "l", "w"]
| },
@@ -5387,7 +5387,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidPatch",
| "description": "Resetting rights and partial updates cannot be done in the same method call"
| }
@@ -5414,7 +5414,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/${BOB.asString()}": ["r"],
| "sharedWith/${DAVID.asString()}": ["r"]
| }
@@ -5447,9 +5447,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
- | "description": "${mailboxId.serialize()} can not be found"
+ | "description": "${mailboxId.serialize} can not be found"
| }
| }
| }, "c1"]
@@ -5479,7 +5479,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "sharedWith/${BOB.asString()}": ["r"],
| "sharedWith/${DAVID.asString()}": ["r"]
| }
@@ -5512,7 +5512,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Invalid change to a delegated mailbox"
| }
@@ -5543,8 +5543,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -5553,7 +5553,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -5582,16 +5582,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "mailbox",
- | "parentId": "${parentId.serialize()}"
+ | "parentId": "${parentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -5613,8 +5613,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${mailboxId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${mailboxId.serialize}"
| }
| }
| },
@@ -5647,7 +5647,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "A mailbox parentId property can not be set to itself or one of its child",
| "properties": [
@@ -5678,8 +5678,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${childId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${childId.serialize}"
| }
| }
| },
@@ -5712,9 +5712,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
- | "description": "${mailboxId.serialize()} parentId property cannot be updated as this mailbox has child mailboxes",
+ | "description": "${mailboxId.serialize} parentId property cannot be updated as this mailbox has child mailboxes",
| "properties": [
| "parentId"
| ]
@@ -5743,8 +5743,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${newParentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${newParentId.serialize}"
| }
| }
| },
@@ -5753,7 +5753,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -5782,16 +5782,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "mailbox",
- | "parentId": "${newParentId.serialize()}"
+ | "parentId": "${newParentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -5814,8 +5814,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}",
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}",
| "name": "newName"
| }
| }
@@ -5825,7 +5825,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -5854,16 +5854,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "newName",
- | "parentId": "${parentId.serialize()}"
+ | "parentId": "${parentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -5887,8 +5887,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${newParentId.serialize()}",
+ | "${mailboxId.serialize}": {
+ | "parentId": "${newParentId.serialize}",
| "name": "newName"
| }
| }
@@ -5898,7 +5898,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -5927,16 +5927,16 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "newName",
- | "parentId": "${newParentId.serialize()}"
+ | "parentId": "${newParentId.serialize}"
| }],
| "notFound": []
| }, "c2"]
@@ -5960,7 +5960,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": null
| }
| }
@@ -5970,7 +5970,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -5999,14 +5999,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "mailbox"
| }],
| "notFound": []
@@ -6030,7 +6030,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": null,
| "name": "newName"
| }
@@ -6041,7 +6041,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -6070,14 +6070,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "newName"
| }],
| "notFound": []
@@ -6102,7 +6102,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": null,
| "name": "newName"
| }
@@ -6113,7 +6113,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -6142,14 +6142,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "newName"
| }],
| "notFound": []
@@ -6172,7 +6172,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "name": "mailbox"
| }
| }
@@ -6182,7 +6182,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -6211,14 +6211,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "mailbox"
| }],
| "notFound": []
@@ -6243,8 +6243,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6253,7 +6253,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c2"]
@@ -6282,15 +6282,15 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
- | "parentId": "${parentId.serialize()}",
+ | "id": "${mailboxId.serialize}",
+ | "parentId": "${parentId.serialize}",
| "name": "mailbox"
| }],
| "notFound": []
@@ -6324,7 +6324,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": "#C42"
| }
| }
@@ -6334,7 +6334,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${mailboxId.serialize()}"],
+ | "ids": ["${mailboxId.serialize}"],
| "properties": ["id", "name", "parentId"]
| },
| "c3"
@@ -6358,7 +6358,7 @@ trait MailboxSetMethodContract {
val parentId: String = server.getProbe(classOf[MailboxProbeImpl])
.getMailboxId("#private", BOB.asString(), "parent")
- .serialize()
+ .serialize
assertThatJson(response).isEqualTo(
s"""{
@@ -6394,14 +6394,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c2"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${mailboxId.serialize()}",
+ | "id": "${mailboxId.serialize}",
| "name": "mailbox",
| "parentId": "$parentId"
| }],
@@ -6424,7 +6424,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": ["invalid"]
| }
| }
@@ -6480,7 +6480,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "parentId": "#C42"
| }
| }
@@ -6537,8 +6537,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6569,9 +6569,9 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
- | "description": "${parentId.serialize()} can not be found"
+ | "description": "${parentId.serialize} can not be found"
| }
| }
| }, "c2"]
@@ -6594,8 +6594,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6626,7 +6626,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Mailbox with name=#private:bob@domain.tld:parent.mailbox already exists.",
| "properties":["parentId"]
@@ -6652,8 +6652,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}",
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}",
| "name": "newMailbox"
| }
| }
@@ -6685,7 +6685,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "invalidArguments",
| "description": "Mailbox with name=#private:bob@domain.tld:parent.newMailbox already exists.",
| "properties":["name", "parentId"]
@@ -6717,8 +6717,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6749,7 +6749,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
| "description": "#private:andre@domain.tld:parent.mailbox"
| }
@@ -6779,8 +6779,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6811,7 +6811,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
| "description": "#private:andre@domain.tld:mailbox"
| }
@@ -6841,8 +6841,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6873,7 +6873,7 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "notUpdated": {
- | "${mailboxId.serialize()}": {
+ | "${mailboxId.serialize}": {
| "type": "notFound",
| "description": "#private:andre@domain.tld:parent.mailbox"
| }
@@ -6903,8 +6903,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -6913,7 +6913,7 @@ trait MailboxSetMethodContract {
| ["Mailbox/get",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["${parentId.serialize()}"],
+ | "ids": ["${parentId.serialize}"],
| "properties": ["id", "name", "parentId", "rights"]
| },
| "c2"]
@@ -6942,14 +6942,14 @@ trait MailboxSetMethodContract {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "newState": "000001",
| "updated": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| }, "c1"],
| ["Mailbox/get", {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "state": "000001",
| "list": [{
- | "id": "${parentId.serialize()}",
+ | "id": "${parentId.serialize}",
| "name": "parent",
| "rights": {
| "${ANDRE.asString()}": ["l"]
@@ -6977,8 +6977,8 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {
- | "parentId": "${parentId.serialize()}"
+ | "${mailboxId.serialize}": {
+ | "parentId": "${parentId.serialize}"
| }
| }
| },
@@ -7033,7 +7033,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| },
| "c1"]]
@@ -7073,7 +7073,7 @@ trait MailboxSetMethodContract {
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "update": {
- | "${mailboxId.serialize()}": {}
+ | "${mailboxId.serialize}": {}
| }
| },
| "c1"]]
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 13/13: JAMES-3386 add test to ensure blank mailbox
paths are not allowed in jmap draft
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 409f94c76b2e7f41cb9e66d702ff85a7e24f52dc
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Sep 23 11:12:17 2020 +0200
JAMES-3386 add test to ensure blank mailbox paths are not allowed in jmap draft
---
.../integration/SetMailboxesMethodTest.java | 61 ++++++++++++++++++++++
.../contract/MailboxSetMethodContract.scala | 8 +--
2 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMailboxesMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMailboxesMethodTest.java
index b2d0de0..ca325a9 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMailboxesMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMailboxesMethodTest.java
@@ -215,6 +215,36 @@ public abstract class SetMailboxesMethodTest {
}
@Test
+ public void setMailboxesCreateShouldFailWhenBlankName() {
+ String creationId = "create-id01";
+ String requestBody =
+ "[" +
+ " [ \"setMailboxes\"," +
+ " {" +
+ " \"create\": {" +
+ " \"" + creationId + "\" : {" +
+ " \"name\" : \" \"" +
+ " }" +
+ " }" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.asString())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("mailboxesSet"))
+ .body(ARGUMENTS + ".notCreated", hasKey(creationId))
+ .body(ARGUMENTS + ".notCreated." + creationId + ".type", is("invalidArguments"))
+ .body(ARGUMENTS + ".notCreated." + creationId + ".description", is("'#private:username@domain.tld: ' has an empty part within its mailbox name considering . as a delimiter"));
+ }
+
+ @Test
public void setMailboxesUpdateShouldFailWhenOverLimitName() throws Exception {
String overLimitName = StringUtils.repeat("a", MailboxManager.MAX_MAILBOX_NAME_LENGTH + 1);
MailboxId mailboxId = mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, username.asString(), "myBox");
@@ -246,6 +276,37 @@ public abstract class SetMailboxesMethodTest {
assertThat(mailboxProbe.listSubscriptions(username.asString())).doesNotContain(overLimitName);
}
+ @Test
+ public void setMailboxesUpdateShouldFailWhenBlankName() throws Exception {
+ MailboxId mailboxId = mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, username.asString(), "myBox");
+ String requestBody =
+ "[" +
+ " [ \"setMailboxes\"," +
+ " {" +
+ " \"update\": {" +
+ " \"" + mailboxId.serialize() + "\" : {" +
+ " \"name\" : \" \"" +
+ " }" +
+ " }" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+ given()
+ .header("Authorization", accessToken.asString())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("mailboxesSet"))
+ .body(ARGUMENTS + ".notUpdated", hasKey(mailboxId.serialize()))
+ .body(ARGUMENTS + ".notUpdated." + mailboxId.serialize() + ".type", is("invalidArguments"))
+ .body(ARGUMENTS + ".notUpdated." + mailboxId.serialize() + ".description", is("'#private:username@domain.tld: ' has an empty part within its mailbox name considering . as a delimiter"));
+
+ assertThat(mailboxProbe.listSubscriptions(username.asString())).doesNotContain(" ");
+ }
+
@Category(BasicFeature.class)
@Test
public void userShouldBeSubscribedOnCreatedMailboxWhenCreateMailbox() throws Exception {
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 dca59bd..4434d3f 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
@@ -738,9 +738,9 @@ trait MailboxSetMethodContract {
`given`
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.body(request)
- .when
+ .when
.post
- .`then`
+ .`then`
.log().ifValidationFails()
.statusCode(SC_OK)
.contentType(JSON)
@@ -794,9 +794,9 @@ trait MailboxSetMethodContract {
`given`
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.body(request)
- .when
+ .when
.post
- .`then`
+ .`then`
.log().ifValidationFails()
.statusCode(SC_OK)
.contentType(JSON)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 12/13: JAMES-3386 do not allow blank mailbox path
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 9a85562e2506f0710304c876eb8b1917a4faa47f
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Sep 23 10:29:36 2020 +0200
JAMES-3386 do not allow blank mailbox path
---
.../apache/james/mailbox/model/MailboxPath.java | 13 +-
.../james/mailbox/model/MailboxPathTest.java | 54 +++++++
.../contract/MailboxSetMethodContract.scala | 167 +++++++++++++++++++++
.../scala/org/apache/james/jmap/mail/Mailbox.scala | 5 +-
.../apache/james/jmap/model/MailboxFactory.scala | 5 +-
5 files changed, 234 insertions(+), 10 deletions(-)
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java
index 3d29587..58044a7 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java
@@ -25,6 +25,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
+import org.apache.commons.lang3.StringUtils;
import org.apache.james.core.Username;
import org.apache.james.mailbox.DefaultMailboxes;
import org.apache.james.mailbox.MailboxSession;
@@ -35,7 +36,6 @@ import org.apache.james.mailbox.exception.TooLongMailboxNameException;
import com.google.common.base.CharMatcher;
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;
@@ -114,7 +114,7 @@ public class MailboxPath {
}
public MailboxPath child(String childName, char delimiter) {
- Preconditions.checkArgument(!Strings.isNullOrEmpty(childName), "'childName' should not be null or empty");
+ Preconditions.checkArgument(!StringUtils.isBlank(childName), "'childName' should not be null or empty");
Preconditions.checkArgument(!childName.contains(String.valueOf(delimiter)), "'childName' should not contain delimiter");
return new MailboxPath(namespace, user, name + delimiter + childName);
@@ -194,10 +194,11 @@ public class MailboxPath {
boolean hasEmptyNameInHierarchy(char pathDelimiter) {
String delimiterString = String.valueOf(pathDelimiter);
- return this.name.isEmpty()
- || this.name.contains(delimiterString + delimiterString)
- || this.name.startsWith(delimiterString)
- || this.name.endsWith(delimiterString);
+ String nameWithoutSpaces = name.replaceAll("\\s", "");
+ return StringUtils.isBlank(nameWithoutSpaces)
+ || nameWithoutSpaces.contains(delimiterString + delimiterString)
+ || nameWithoutSpaces.startsWith(delimiterString)
+ || nameWithoutSpaces.endsWith(delimiterString);
}
public String asString() {
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxPathTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxPathTest.java
index a4d63c7..a160b3c 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxPathTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxPathTest.java
@@ -69,6 +69,12 @@ class MailboxPathTest {
}
@Test
+ void getNameShouldNoopWhenBlank() {
+ assertThat(MailboxPath.forUser(USER, " ").getName('.'))
+ .isEqualTo(" ");
+ }
+
+ @Test
void getHierarchyLevelsShouldBeOrdered() {
assertThat(MailboxPath.forUser(USER, "inbox.folder.subfolder")
.getHierarchyLevels('.'))
@@ -100,6 +106,13 @@ class MailboxPathTest {
}
@Test
+ void childShouldThrowWhenBlank() {
+ MailboxPath path = MailboxPath.forUser(USER, "folder");
+ assertThatThrownBy(() -> path.child(" ", '.'))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
void childShouldThrowWhenContainsDelimiter() {
MailboxPath path = MailboxPath.forUser(USER, "folder");
assertThatThrownBy(() -> path.child("a.b", '.'))
@@ -123,6 +136,14 @@ class MailboxPathTest {
}
@Test
+ void getHierarchyLevelsShouldReturnPathWhenBlankName() {
+ assertThat(MailboxPath.forUser(USER, " ")
+ .getHierarchyLevels('.'))
+ .containsExactly(
+ MailboxPath.forUser(USER, " "));
+ }
+
+ @Test
void getHierarchyLevelsShouldReturnPathWhenNullName() {
assertThat(MailboxPath.forUser(USER, null)
.getHierarchyLevels('.'))
@@ -208,6 +229,13 @@ class MailboxPathTest {
}
@Test
+ void hasEmptyNameInHierarchyShouldBeTrueIfBlankPath() {
+ assertThat(MailboxPath.forUser(USER, " ")
+ .hasEmptyNameInHierarchy('.'))
+ .isTrue();
+ }
+
+ @Test
void hasEmptyNameInHierarchyShouldBeTrueIfPathWithTwoEmptyNames() {
assertThat(MailboxPath.forUser(USER, ".")
.hasEmptyNameInHierarchy('.'))
@@ -220,6 +248,12 @@ class MailboxPathTest {
.hasEmptyNameInHierarchy('.'))
.isTrue();
}
+ @Test
+ void hasEmptyNameInHierarchyShouldBeTrueIfPathWithABlankNameBetweenTwoNames() {
+ assertThat(MailboxPath.forUser(USER, "a. .b")
+ .hasEmptyNameInHierarchy('.'))
+ .isTrue();
+ }
@Test
void hasEmptyNameInHierarchyShouldBeTrueIfPathWithHeadingEmptyNames() {
@@ -229,6 +263,13 @@ class MailboxPathTest {
}
@Test
+ void hasEmptyNameInHierarchyShouldBeTrueIfPathWithHeadingBlankName() {
+ assertThat(MailboxPath.forUser(USER, " .a")
+ .hasEmptyNameInHierarchy('.'))
+ .isTrue();
+ }
+
+ @Test
void hasEmptyNameInHierarchyShouldBeTrueIfPathWithATrailingEmptyName() {
assertThat(MailboxPath.forUser(USER, "a.")
.hasEmptyNameInHierarchy('.'))
@@ -236,11 +277,24 @@ class MailboxPathTest {
}
@Test
+ void hasEmptyNameInHierarchyShouldBeTrueIfPathWithATrailingBlankName() {
+ assertThat(MailboxPath.forUser(USER, "a. ")
+ .hasEmptyNameInHierarchy('.'))
+ .isTrue();
+ }
+
+ @Test
void hasEmptyNameInHierarchyShouldBeTrueIfPathWithTrailingEmptyNames() {
assertThat(MailboxPath.forUser(USER, "a..")
.hasEmptyNameInHierarchy('.'))
.isTrue();
}
+ @Test
+ void hasEmptyNameInHierarchyShouldBeTrueIfPathWithTrailingBlankNames() {
+ assertThat(MailboxPath.forUser(USER, "a. . ")
+ .hasEmptyNameInHierarchy('.'))
+ .isTrue();
+ }
@Test
void assertAcceptableShouldThrowOnDoubleSeparator() {
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 d87adb0..dca59bd 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
@@ -332,6 +332,60 @@ trait MailboxSetMethodContract {
}
@Test
+ def updateShouldFailWhenModifyingNameToBlank(server: GuiceJamesServer): Unit = {
+ val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ .createMailbox(MailboxPath.forUser(BOB, "mailbox"))
+ val request =
+ s"""
+ |{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | ["Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "update": {
+ | "${mailboxId.serialize()}": {
+ | "name": " "
+ | }
+ | }
+ | },
+ | "c1"]]
+ |}
+ |""".stripMargin
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .log().ifValidationFails()
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [
+ | ["Mailbox/set", {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "notUpdated": {
+ | "${mailboxId.serialize()}": {
+ | "type": "invalidArguments",
+ | "description": "'#private:bob@domain.tld: ' has an empty part within its mailbox name considering . as a delimiter",
+ | "properties":["name"]
+ | }
+ | }
+ | }, "c1"]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
def createShouldFailWhenNameWithDelimiter(): Unit = {
val request =
"""
@@ -658,6 +712,119 @@ trait MailboxSetMethodContract {
}
@Test
+ def mailboxSetShouldReturnNotCreatedWhenNameIsEmpty(): Unit = {
+ val request =
+ """
+ |{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "create": {
+ | "C42": {
+ | "name": ""
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}
+ |""".stripMargin
+
+ val response: String =
+ `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .log().ifValidationFails()
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "notCreated": {
+ | "C42": {
+ | "type": "invalidArguments",
+ | "description": "'/name' property in mailbox object is not valid: Predicate isEmpty() did not fail."
+ | }
+ | }
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def mailboxSetShouldReturnNotCreatedWhenNameIsBlank(): Unit = {
+ val request =
+ """
+ |{
+ | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+ | "methodCalls": [
+ | [
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "create": {
+ | "C42": {
+ | "name": " "
+ | }
+ | }
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}
+ |""".stripMargin
+
+ val response: String =
+ `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .log().ifValidationFails()
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response).isEqualTo(
+ s"""{
+ | "sessionState": "75128aab4b1b",
+ | "methodResponses": [[
+ | "Mailbox/set",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "newState": "000001",
+ | "notCreated": {
+ | "C42": {
+ | "type": "invalidArguments",
+ | "description": "'#private:bob@domain.tld: ' has an empty part within its mailbox name considering . as a delimiter",
+ | "properties":["name"]
+ | }
+ | }
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ }
+
+ @Test
def mailboxSetShouldReturnNotCreatedWhenUnknownParameter(): Unit = {
val request =
"""
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
index 28b094c..e9f0985 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
@@ -29,6 +29,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
import org.apache.james.jmap.model.{CapabilityIdentifier, Properties}
import org.apache.james.mailbox.Role
+import org.apache.james.mailbox.exception.MailboxNameException
import org.apache.james.mailbox.model.MailboxId
case class MayReadItems(value: Boolean) extends AnyVal
@@ -120,9 +121,9 @@ object MailboxName {
type MailboxNameConstraint = NonEmpty
type MailboxName = String Refined MailboxNameConstraint
- def validate(value: String): Either[IllegalArgumentException, MailboxName] =
+ def validate(value: String): Either[MailboxNameException, MailboxName] =
refined.refineV[MailboxNameConstraint](value) match {
- case Left(error) => Left(new IllegalArgumentException(error))
+ case Left(error) => Left(new MailboxNameException(error))
case scala.Right(value) => scala.Right(value)
}
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
index b18544f..4ed2cfb 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
@@ -24,6 +24,7 @@ import org.apache.james.jmap.mail.MailboxName.MailboxName
import org.apache.james.jmap.mail._
import org.apache.james.jmap.utils.quotas.QuotaLoader
import org.apache.james.mailbox._
+import org.apache.james.mailbox.exception.MailboxNameException
import org.apache.james.mailbox.model.MailboxACL.EntryKey
import org.apache.james.mailbox.model.{MailboxCounters, MailboxId, MailboxMetaData, MailboxPath, MailboxACL => JavaMailboxACL}
import reactor.core.scala.publisher.SMono
@@ -32,12 +33,12 @@ import scala.jdk.CollectionConverters._
import scala.jdk.OptionConverters._
object MailboxValidation {
- private def retrieveMailboxName(mailboxPath: MailboxPath, pathDelimiter: Char): Either[IllegalArgumentException, MailboxName] =
+ private def retrieveMailboxName(mailboxPath: MailboxPath, pathDelimiter: Char): Either[MailboxNameException, MailboxName] =
mailboxPath.getName
.split(pathDelimiter)
.lastOption match {
case Some(name) => MailboxName.validate(name)
- case None => Left(new IllegalArgumentException("No name for the mailbox found"))
+ case None => Left(new MailboxNameException("No name for the mailbox found"))
}
def validate(mailboxPath: MailboxPath,
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org
[james-project] 01/13: JAMES-3379 Email/get specific parsed
headers: asGroupedAddresses
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 546550f1cd65bb03538bf9515ed7995b21909561
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Mon Sep 21 18:05:55 2020 +0700
JAMES-3379 Email/get specific parsed headers: asGroupedAddresses
---
.../rfc8621/contract/EmailGetMethodContract.scala | 206 ++++++++++++++++++++-
.../james/jmap/json/EmailGetSerializer.scala | 9 +-
.../scala/org/apache/james/jmap/mail/Email.scala | 4 +
.../apache/james/jmap/mail/EmailAddressGroup.scala | 24 +++
.../org/apache/james/jmap/mail/EmailHeader.scala | 34 ++++
5 files changed, 274 insertions(+), 3 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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index 6f71695..601ba30 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -28,7 +28,6 @@ import io.restassured.RestAssured.{`given`, requestSpecification}
import io.restassured.http.ContentType.JSON
import javax.mail.Flags
import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
-import net.javacrumbs.jsonunit.core.Option
import net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER
import net.javacrumbs.jsonunit.core.internal.Options
import org.apache.http.HttpStatus.SC_OK
@@ -5945,7 +5944,7 @@ trait EmailGetMethodContract {
}
@Test
- def emailGetShouldReturnEmptyWhenCannotParseAsAdresses(server: GuiceJamesServer): Unit = {
+ def emailGetShouldReturnEmptyWhenCannotParseAsAddresses(server: GuiceJamesServer): Unit = {
val bobPath = MailboxPath.inbox(BOB)
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
val alicePath = MailboxPath.inbox(ALICE)
@@ -5995,4 +5994,207 @@ trait EmailGetMethodContract {
| "header:To:asAddresses": []
}""".stripMargin)
}
+
+ @Test
+ def emailGetShouldReturnGroupedAddresses(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("To", "\" user1 \" <us...@domain.tld>, \" user2 \" <us...@domain.tld>, Friends: <us...@domain.tld>, \"user4\" <us...@domain.tld>;"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:To:asGroupedAddresses"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:To:asGroupedAddresses": [
+ | {
+ | "name": null,
+ | "addresses": [
+ | {
+ | "name": "user1",
+ | "email": "user1@domain.tld"
+ | },
+ | {
+ | "name": "user2",
+ | "email": "user2@domain.tld"
+ | }
+ | ]
+ | },
+ | {
+ | "name": "Friends",
+ | "addresses": [
+ | {
+ | "email": "user3@domain.tld"
+ | },
+ | {
+ | "name": "user4",
+ | "email": "user4@domain.tld"
+ | }
+ | ]
+ | }
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnAsGroupedAddressesWithoutGroupInfo(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("To", "\" user1 \" <us...@domain.tld>, \" user2 \" <us...@domain.tld>, <us...@domain.tld>, \"user4\" <us...@domain.tld>"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:To:asGroupedAddresses"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:To:asGroupedAddresses": [
+ | {
+ | "name": null,
+ | "addresses": [
+ | {
+ | "name": "user1",
+ | "email": "user1@domain.tld"
+ | },
+ | {
+ | "name": "user2",
+ | "email": "user2@domain.tld"
+ | },
+ | {
+ | "email": "user3@domain.tld"
+ | },
+ | {
+ | "name": "user4",
+ | "email": "user4@domain.tld"
+ | }
+ | ]
+ | }
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def emailGetShouldReturnEmptyWhenCannotParseAsGroupedAddresses(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ val alicePath = MailboxPath.inbox(ALICE)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(ANDRE.asString())
+ .setFrom(ANDRE.asString())
+ .addField(new RawField("To", "blahblah"))
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+ val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+ .getMessageId
+
+ val response = `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["${messageId.serialize}"],
+ | "properties": ["header:To:asGroupedAddresses"]
+ | },
+ | "c1"]]
+ |}""".stripMargin)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].list[0]")
+ .isEqualTo(
+ s"""{
+ | "id": "${messageId.serialize}",
+ | "header:To:asGroupedAddresses": []
+ }""".stripMargin)
+ }
}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index 475e1df..ae33dc1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
package org.apache.james.jmap.json
import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, Disposition, EmailAddress, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, HasAttachment, HeaderMessageId, [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, Charset, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, GroupName, Gr [...]
import org.apache.james.jmap.model._
import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
import play.api.libs.functional.syntax._
@@ -57,10 +57,17 @@ object EmailGetSerializer {
private implicit val rawHeaderWrites: Writes[RawHeaderValue] = Json.valueWrites[RawHeaderValue]
private implicit val textHeaderWrites: Writes[TextHeaderValue] = Json.valueWrites[TextHeaderValue]
private implicit val addressesHeaderWrites: Writes[AddressesHeaderValue] = Json.valueWrites[AddressesHeaderValue]
+ private implicit val GroupNameWrites: Writes[GroupName] = Json.valueWrites[GroupName]
+ private implicit val emailAddressGroupWrites: Writes[EmailAddressGroup] = (o: EmailAddressGroup) =>
+ Json.obj(
+ "name" -> Json.toJson(o.name),
+ "addresses" -> Json.toJson(o.addresses))
+ private implicit val groupedAddressesHeaderWrites: Writes[GroupedAddressesHeaderValue] = Json.valueWrites[GroupedAddressesHeaderValue]
private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
case headerValue: RawHeaderValue => Json.toJson[RawHeaderValue](headerValue)
case headerValue: TextHeaderValue => Json.toJson[TextHeaderValue](headerValue)
case headerValue: AddressesHeaderValue => Json.toJson[AddressesHeaderValue](headerValue)
+ case headerValue: GroupedAddressesHeaderValue => Json.toJson[GroupedAddressesHeaderValue](headerValue)
}
private implicit val headersWrites: Writes[EmailHeader] = Json.writes[EmailHeader]
private implicit val bodyValueWrites: Writes[EmailBodyValue] = Json.writes[EmailBodyValue]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 560e509..ece0fe3 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -166,6 +166,7 @@ object ParseOptions {
case "asRaw" => Some(AsRaw)
case "asText" => Some(AsText)
case "asAddresses" => Some(AsAddresses)
+ case "asGroupedAddresses" => Some(AsGroupedAddresses)
case _ => None
}
}
@@ -182,6 +183,9 @@ case object AsText extends ParseOption {
case object AsAddresses extends ParseOption {
override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(AddressesHeaderValue.from(field))
}
+case object AsGroupedAddresses extends ParseOption {
+ override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = Some(GroupedAddressesHeaderValue.from(field))
+}
case class HeaderMessageId(value: String) extends AnyVal
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
new file mode 100644
index 0000000..b423996
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
@@ -0,0 +1,24 @@
+/****************************************************************
+ * 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.mail
+
+case class GroupName(value: String) extends AnyVal
+
+case class EmailAddressGroup(name: Option[GroupName], addresses: List[EmailAddress])
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 98133ec..e1da31b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -22,10 +22,13 @@ package org.apache.james.jmap.mail
import java.nio.charset.StandardCharsets.US_ASCII
import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
+import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
import org.apache.james.mime4j.field.AddressListFieldImpl
import org.apache.james.mime4j.stream.Field
import org.apache.james.mime4j.util.MimeUtil
+import scala.jdk.CollectionConverters._
+
object EmailHeader {
def apply(field: Field): EmailHeader = EmailHeader(EmailHeaderName(field.getName), RawHeaderValue.from(field))
}
@@ -42,11 +45,42 @@ object AddressesHeaderValue extends EmailHeaderValue {
def from(field: Field): AddressesHeaderValue = AddressesHeaderValue(EmailAddress.from(AddressListFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getAddressList))
}
+object GroupedAddressesHeaderValue extends EmailHeaderValue {
+ def from(field: Field): GroupedAddressesHeaderValue = {
+ val addresses: List[Mime4jAddress] =
+ Option(AddressListFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getAddressList)
+ .getOrElse(new AddressList())
+ .asScala
+ .toList
+
+ val groups: List[EmailAddressGroup] = addresses
+ .flatMap({
+ case group: Group => Some(group)
+ case _ => None
+ })
+ .map(group => EmailAddressGroup(Some(GroupName(group.getName)), EmailAddress.from(group.getMailboxes)))
+
+ val addressesWithoutGroup: List[EmailAddress] = addresses
+ .flatMap({
+ case mailbox: Mime4jMailbox => Some(mailbox)
+ case _ => None
+ })
+ .map(EmailAddress.from(_))
+
+ if (addresses.isEmpty) {
+ GroupedAddressesHeaderValue(List())
+ } else {
+ GroupedAddressesHeaderValue(List(EmailAddressGroup(None, addressesWithoutGroup)) ++ groups)
+ }
+ }
+}
+
case class EmailHeaderName(value: String) extends AnyVal
sealed trait EmailHeaderValue
case class RawHeaderValue(value: String) extends EmailHeaderValue
case class TextHeaderValue(value: String) extends EmailHeaderValue
case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderValue
+case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue
case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org