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 673e909b50c01c8b70c9faaa738588140ff807ad Author: Benoit Tellier <[email protected]> AuthorDate: Mon Nov 16 14:19:43 2020 +0700 JAMES-3440 JMAP RFC-8621 should use EmailQueryView when enabled --- .../apache/james/modules/TestJMAPServerModule.java | 1 + .../DistributedEmailQueryMethodNoViewTest.java | 62 ++++++++++++++++ .../src/test/resources/listeners.xml | 4 + .../contract/EmailQueryMethodContract.scala | 73 ++++++++++++++++++ .../memory/MemoryEmailQueryMethodNoViewTest.java | 58 +++++++++++++++ .../src/test/resources/listeners.xml | 4 + .../org/apache/james/jmap/mail/EmailQuery.scala | 46 +++++++++++- .../james/jmap/method/EmailQueryMethod.scala | 86 +++++++++++++++++++--- 8 files changed, 320 insertions(+), 14 deletions(-) diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/modules/TestJMAPServerModule.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/modules/TestJMAPServerModule.java index d004632..83496ec 100644 --- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/modules/TestJMAPServerModule.java +++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/modules/TestJMAPServerModule.java @@ -116,6 +116,7 @@ public class TestJMAPServerModule extends AbstractModule { return JMAPConfiguration.builder() .enable() .randomPort() + .enableEmailQueryView() .build(); } diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java new file mode 100644 index 0000000..235118b --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java @@ -0,0 +1,62 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.jmap.rfc8621.distributed; + +import org.apache.james.CassandraExtension; +import org.apache.james.CassandraRabbitMQJamesConfiguration; +import org.apache.james.CassandraRabbitMQJamesServerMain; +import org.apache.james.DockerElasticSearchExtension; +import org.apache.james.JamesServerBuilder; +import org.apache.james.JamesServerExtension; +import org.apache.james.jmap.JMAPConfiguration; +import org.apache.james.jmap.rfc8621.contract.EmailQueryMethodContract; +import org.apache.james.modules.AwsS3BlobStoreExtension; +import org.apache.james.modules.RabbitMQExtension; +import org.apache.james.modules.TestJMAPServerModule; +import org.apache.james.modules.blobstore.BlobStoreConfiguration; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class DistributedEmailQueryMethodNoViewTest implements EmailQueryMethodContract { + public static final DockerElasticSearchExtension ELASTIC_SEARCH_EXTENSION = new DockerElasticSearchExtension(); + + @RegisterExtension + static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir -> + CassandraRabbitMQJamesConfiguration.builder() + .workingDirectory(tmpDir) + .configurationFromClasspath() + .blobStore(BlobStoreConfiguration.builder() + .s3() + .disableCache() + .deduplication()) + .build()) + .extension(ELASTIC_SEARCH_EXTENSION) + .extension(new CassandraExtension()) + .extension(new RabbitMQExtension()) + .extension(new AwsS3BlobStoreExtension()) + .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration) + .overrideWith(new TestJMAPServerModule()) + .overrideWith(binder -> binder.bind(JMAPConfiguration.class) + .toInstance(JMAPConfiguration.builder() + .enable() + .randomPort() + .disableEmailQueryView() + .build()))) + .build(); +} diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml index ff2e517..1ff4055 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml +++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml @@ -46,4 +46,8 @@ <name>second</name> </configuration> </listener> + <listener> + <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class> + <async>true</async> + </listener> </listeners> \ No newline at end of file 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 9f9078a..ff8ec97 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 @@ -1629,6 +1629,78 @@ trait EmailQueryMethodContract { } @Test + def listMailsShouldBeSortedByDescendingOrderOfSentAtAndInMailbox(server: GuiceJamesServer): Unit = { + val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB)) + val message: Message = Message.Builder + .of + .setSubject("test") + .setBody("testmail", StandardCharsets.UTF_8) + .build + + val requestDateMessage1 = Date.from(ZonedDateTime.now().minusDays(1).toInstant) + val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, MailboxPath.inbox(BOB), + AppendCommand.builder() + .withInternalDate(requestDateMessage1) + .build(message)) + .getMessageId + + val requestDateMessage2 = Date.from(ZonedDateTime.now().minusDays(2).toInstant) + val messageId2 = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, MailboxPath.inbox(BOB), + AppendCommand.builder() + .withInternalDate(requestDateMessage2) + .build(message)) + .getMessageId + + val requestDateMessage3 = Date.from(ZonedDateTime.now().minusDays(3).toInstant) + val messageId3 = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, MailboxPath.inbox(BOB), + AppendCommand.builder() + .withInternalDate(requestDateMessage3) + .build(message)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "inMailbox": "${mailboxId.serialize}" + | }, + | "comparator": [{ + | "property":"sentAt", + | "isAscending": false + | }] + | }, + | "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) + .inPath("$.methodResponses[0][1].ids") + .isEqualTo(s"""["${messageId1.serialize}", "${messageId2.serialize}", "${messageId3.serialize}"]""") + } + } + + @Test def listMailsShouldBeSortedByAscendingOrderOfSentAt(server: GuiceJamesServer): Unit = { server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB)) val otherMailboxPath = MailboxPath.forUser(BOB, "other") @@ -2967,6 +3039,7 @@ trait EmailQueryMethodContract { .isEqualTo(s"""["${messageId1.serialize}"]""") } } + @Test def shouldListMailsReceivedBeforeADateInclusively(server: GuiceJamesServer): Unit = { val message: Message = buildTestMessage diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodNoViewTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodNoViewTest.java new file mode 100644 index 0000000..8cbe04a --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodNoViewTest.java @@ -0,0 +1,58 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.jmap.rfc8621.memory; + +import static org.apache.james.MemoryJamesServerMain.IN_MEMORY_SERVER_AGGREGATE_MODULE; + +import org.apache.james.GuiceJamesServer; +import org.apache.james.JamesServerBuilder; +import org.apache.james.JamesServerExtension; +import org.apache.james.jmap.JMAPConfiguration; +import org.apache.james.jmap.rfc8621.contract.EmailQueryMethodContract; +import org.apache.james.modules.TestJMAPServerModule; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class MemoryEmailQueryMethodNoViewTest implements EmailQueryMethodContract { + @RegisterExtension + static JamesServerExtension testExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider()) + .server(configuration -> GuiceJamesServer.forConfiguration(configuration) + .combineWith(IN_MEMORY_SERVER_AGGREGATE_MODULE) + .overrideWith(new TestJMAPServerModule()) + .overrideWith(binder -> binder.bind(JMAPConfiguration.class) + .toInstance(JMAPConfiguration.builder() + .enable() + .randomPort() + .disableEmailQueryView() + .build()))) + .build(); + + @Test + @Override + @Disabled("JAMES-3377 Not supported for in-memory test") + public void emailQueryFilterByTextShouldIgnoreMarkupsInHtmlBody(GuiceJamesServer server) {} + + @Test + @Override + @Disabled("JAMES-3377 Not supported for in-memory test" + + "In memory do not attempt message parsing a performs a full match on the raw message content") + public void emailQueryFilterByTextShouldIgnoreAttachmentContent(GuiceJamesServer server) {} +} diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml index 59e3fec..a1a139d 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/listeners.xml @@ -43,4 +43,8 @@ <name>second</name> </configuration> </listener> + <listener> + <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class> + <async>true</async> + </listener> </listeners> \ No newline at end of file diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala index 7f154e1..462fff0 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala @@ -35,7 +35,11 @@ case class UnsupportedFilterException(unsupportedFilter: String) extends Unsuppo case class UnsupportedNestingException(message: String) extends UnsupportedOperationException case class UnsupportedRequestParameterException(unsupportedParam: String) extends UnsupportedOperationException -sealed trait FilterQuery +sealed trait FilterQuery { + def inMailboxFilterOnly: Boolean + + def inMailboxAndAfterFilterOnly: Boolean +} sealed trait Operator case object And extends Operator @@ -43,7 +47,11 @@ case object Or extends Operator case object Not extends Operator case class FilterOperator(operator: Operator, - conditions: Seq[FilterQuery]) extends FilterQuery + conditions: Seq[FilterQuery]) extends FilterQuery { + override val inMailboxFilterOnly: Boolean = false + + override val inMailboxAndAfterFilterOnly: Boolean = false +} case class Text(value: String) extends AnyVal case class From(value: String) extends AnyVal @@ -81,7 +89,35 @@ case class FilterCondition(inMailbox: Option[MailboxId], bcc: Option[Bcc], subject: Option[Subject], header: Option[Header], - body: Option[Body]) extends FilterQuery + body: Option[Body]) extends FilterQuery { + private val noOtherFiltersThanInMailboxAndAfter: Boolean = inMailboxOtherThan.isEmpty && + before.isEmpty && + hasKeyword.isEmpty && + notKeyword.isEmpty && + minSize.isEmpty && + maxSize.isEmpty && + hasAttachment.isEmpty && + allInThreadHaveKeyword.isEmpty && + someInThreadHaveKeyword.isEmpty && + noneInThreadHaveKeyword.isEmpty && + text.isEmpty && + from.isEmpty && + to.isEmpty && + cc.isEmpty && + bcc.isEmpty && + subject.isEmpty && + header.isEmpty && + body.isEmpty + + override val inMailboxFilterOnly: Boolean = inMailbox.nonEmpty && + after.isEmpty && + noOtherFiltersThanInMailboxAndAfter + + + override val inMailboxAndAfterFilterOnly: Boolean = inMailbox.nonEmpty && + after.nonEmpty && + noOtherFiltersThanInMailboxAndAfter +} case class EmailQueryRequest(accountId: AccountId, position: Option[PositionUnparsed], @@ -163,6 +199,10 @@ case class IsAscending(sortByASC: Boolean) extends AnyVal { case class Collation(value: String) extends AnyVal +object Comparator { + val SENT_AT_DESC: Comparator = Comparator(SentAtSortProperty, Some(IsAscending.DESCENDING), None) +} + case class Comparator(property: SortProperty, isAscending: Option[IsAscending], collation: Option[Collation]) { 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 6c32948..ae7d822 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 @@ -18,31 +18,40 @@ ****************************************************************/ package org.apache.james.jmap.method +import java.time.ZonedDateTime + import cats.implicits._ import eu.timepit.refined.auto._ import javax.inject.Inject +import org.apache.james.jmap.JMAPConfiguration +import org.apache.james.jmap.api.projections.EmailQueryView import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} import org.apache.james.jmap.core.Invocation.{Arguments, MethodName} import org.apache.james.jmap.core.Limit.Limit import org.apache.james.jmap.core.Position.Position import org.apache.james.jmap.core.{CanCalculateChanges, Invocation, Limit, Position, QueryState} import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer} -import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedRequestParameterException} +import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, FilterCondition, UnsupportedRequestParameterException} import org.apache.james.jmap.routes.SessionSupplier import org.apache.james.jmap.utils.search.MailboxFilter import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter -import org.apache.james.mailbox.model.MultimailboxesSearchQuery +import org.apache.james.mailbox.exception.MailboxNotFoundException +import org.apache.james.mailbox.model.{MailboxId, MessageId, MultimailboxesSearchQuery} import org.apache.james.mailbox.{MailboxManager, MailboxSession} import org.apache.james.metrics.api.MetricFactory +import org.apache.james.util.streams.{Limit => JavaLimit} import play.api.libs.json.{JsError, JsSuccess} import reactor.core.scala.publisher.{SFlux, SMono} +import reactor.core.scheduler.Schedulers import scala.jdk.CollectionConverters._ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, mailboxManager: MailboxManager, val metricFactory: MetricFactory, - val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[EmailQueryRequest] { + val sessionSupplier: SessionSupplier, + val configuration: JMAPConfiguration, + val emailQueryView: EmailQueryView) extends MethodRequiringAccountId[EmailQueryRequest] { override val methodName: MethodName = MethodName("Email/query") override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) @@ -71,17 +80,72 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[EmailQueryRequest] = asEmailQueryRequest(invocation.arguments) - private def executeQuery(mailboxSession: MailboxSession, request: EmailQueryRequest, searchQuery: MultimailboxesSearchQuery, position: Position, limitToUse: Limit): SMono[EmailQueryResponse] = { + private def executeQuery(session: MailboxSession, request: EmailQueryRequest, searchQuery: MultimailboxesSearchQuery, position: Position, limit: Limit): SMono[EmailQueryResponse] = { + val ids: SMono[Seq[MessageId]] = request match { + case request: EmailQueryRequest if matchesInMailboxSortedBySentAt(request) => + queryViewForListingSortedBySentAt(session, position, limit, request) + case request: EmailQueryRequest if matchesInMailboxAfterSortedBySentAt(request) => + queryViewForContentAfterSortedBySentAt(session, position, limit, request) + case _ => executeQueryAgainstSearchIndex(session, searchQuery, position, limit) + } + + ids.map(ids => toResponse(request, position, limit, ids)) + } + + private def queryViewForContentAfterSortedBySentAt(mailboxSession: MailboxSession, position: Position, limitToUse: Limit, request: EmailQueryRequest): SMono[Seq[MessageId]] = { + val condition: FilterCondition = request.filter.get.asInstanceOf[FilterCondition] + val mailboxId: MailboxId = condition.inMailbox.get + val after: ZonedDateTime = condition.after.get.asUTC + SMono.fromCallable(() => mailboxManager.getMailbox(mailboxId, mailboxSession)) + .subscribeOn(Schedulers.elastic()) + .`then`(SFlux.fromPublisher( + emailQueryView.listMailboxContentSinceReceivedAt(mailboxId, after, JavaLimit.from(limitToUse.value))) + .drop(position.value) + .take(limitToUse.value) + .collectSeq()) + .onErrorResume({ + case _: MailboxNotFoundException => SMono.just[Seq[MessageId]](Seq()) + case e => SMono.raiseError[Seq[MessageId]](e) + }) + } + + private def queryViewForListingSortedBySentAt(mailboxSession: MailboxSession, position: Position, limitToUse: Limit, request: EmailQueryRequest): SMono[Seq[MessageId]] = { + val mailboxId: MailboxId = request.filter.get.asInstanceOf[FilterCondition].inMailbox.get + SMono.fromCallable(() => mailboxManager.getMailbox(mailboxId, mailboxSession)) + .subscribeOn(Schedulers.elastic()) + .`then`(SFlux.fromPublisher( + emailQueryView.listMailboxContent(mailboxId, JavaLimit.from(limitToUse.value))) + .drop(position.value) + .take(limitToUse.value) + .collectSeq()) + .onErrorResume({ + case _: MailboxNotFoundException => SMono.just[Seq[MessageId]](Seq()) + case e => SMono.raiseError[Seq[MessageId]](e) + }) + } + + private def matchesInMailboxSortedBySentAt(request: EmailQueryRequest): Boolean = + configuration.isEmailQueryViewEnabled && + request.filter.exists(_.inMailboxFilterOnly) && + request.comparator.contains(Set(Comparator.SENT_AT_DESC)) + + private def matchesInMailboxAfterSortedBySentAt(request: EmailQueryRequest): Boolean = + configuration.isEmailQueryViewEnabled && + request.filter.exists(_.inMailboxAndAfterFilterOnly) && + request.comparator.contains(Set(Comparator.SENT_AT_DESC)) + + private def toResponse(request: EmailQueryRequest, position: Position, limitToUse: Limit, ids: Seq[MessageId]): EmailQueryResponse = + EmailQueryResponse(accountId = request.accountId, + queryState = QueryState.forIds(ids), + canCalculateChanges = CanCalculateChanges.CANNOT, + ids = ids, + position = position, + limit = Some(limitToUse).filterNot(used => request.limit.map(_.value).contains(used.value))) + + private def executeQueryAgainstSearchIndex(mailboxSession: MailboxSession, searchQuery: MultimailboxesSearchQuery, position: Position, limitToUse: Limit): SMono[Seq[MessageId]] = SFlux.fromPublisher(mailboxManager.search(searchQuery, mailboxSession, position.value + limitToUse)) .drop(position.value) .collectSeq() - .map(ids => EmailQueryResponse(accountId = request.accountId, - queryState = QueryState.forIds(ids), - canCalculateChanges = CanCalculateChanges.CANNOT, - ids = ids, - position = position, - limit = Some(limitToUse).filterNot(used => request.limit.map(_.value).contains(used.value)))) - } private def searchQueryFromRequest(request: EmailQueryRequest, capabilities: Set[CapabilityIdentifier], session: MailboxSession): Either[UnsupportedOperationException, MultimailboxesSearchQuery] = { val comparators: List[Comparator] = request.comparator.getOrElse(Set()).toList --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
