This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit b0d172be411edbd6d58dc7bdcf02d3b5449a8cd3 Author: LanKhuat <[email protected]> AuthorDate: Wed Sep 30 14:00:46 2020 +0700 JAMES-3377 Email/query allow filtering by text --- .../contract/EmailQueryMethodContract.scala | 330 ++++++++++++++++++++- .../rfc8621/memory/MemoryEmailQueryMethodTest.java | 6 + .../james/jmap/utils/search/MailboxFilter.scala | 2 +- 3 files changed, 334 insertions(+), 4 deletions(-) diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala index e954bf5..3fdd803 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 @@ -46,13 +46,13 @@ import org.apache.james.mailbox.model.MailboxPath.inbox import org.apache.james.mailbox.model.{MailboxACL, MailboxPath, MessageId} import org.apache.james.mime4j.dom.Message import org.apache.james.mime4j.field.address.DefaultAddressParser -import org.apache.james.mime4j.message.DefaultMessageWriter +import org.apache.james.mime4j.message.{DefaultMessageWriter, MultipartBuilder} import org.apache.james.mime4j.stream.RawField import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl} import org.apache.james.utils.DataProbeImpl import org.awaitility.Awaitility import org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS -import org.junit.jupiter.api.{BeforeEach, Test} +import org.junit.jupiter.api.{BeforeEach, Disabled, Test} import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource} import org.threeten.extra.Seconds @@ -1985,7 +1985,6 @@ trait EmailQueryMethodContract { "allInThreadHaveKeyword", "someInThreadHaveKeyword", "noneInThreadHaveKeyword", - "text", "body" )) def listMailsShouldReturnUnsupportedFilterWhenValidButUnsupported(unsupportedFilter: String): Unit = { @@ -4505,6 +4504,331 @@ trait EmailQueryMethodContract { } } + @Test + def emailQueryShouldSupportTextFilterForHeaders(server: GuiceJamesServer): Unit = { + val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl]) + + mailboxProbe.createMailbox(inbox(BOB)) + + val messageId1: MessageId = mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("a mail") + .setFrom("[email protected]") + .setFrom("[email protected]") + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val messageId2: MessageId = mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("another mail") + .setTo("[email protected]") + .setTo("[email protected]") + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val messageId3: MessageId = mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("another mail") + .addField(new RawField("Cc", "<[email protected]>, <[email protected]>")) + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val messageId4: MessageId = mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("another mail") + .addField(new RawField("Bcc", "<[email protected]>, <[email protected]>")) + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val messageId5: MessageId = mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("Subject with test word") + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + mailboxProbe + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("should not be found mail") + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "text": "test" + | } + | }, + | "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"""[ + | "${messageId5.serialize}", + | "${messageId4.serialize}", + | "${messageId3.serialize}", + | "${messageId2.serialize}", + | "${messageId1.serialize}" + |]""".stripMargin) + } + } + + @Test + def emailQueryShouldSupportTextFilterForTextBody(server: GuiceJamesServer): Unit = { + server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB)) + val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("a mail") + .setBody("This is a test body", StandardCharsets.UTF_8) + .build)) + .getMessageId + + server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("should not be found mail") + .setBody("lorem ipsum", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "text": "test" + | } + | }, + | "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}" + |]""".stripMargin) + } + } + + @Test + def emailQueryShouldSupportTextFilterForHtmlBody(server: GuiceJamesServer): Unit = { + server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB)) + val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("a mail") + .setBody("<body>This is a test body</body>", "html", StandardCharsets.UTF_8) + .build)) + .getMessageId + + server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("should not be found mail") + .setBody("<body>This is another body</body>", "html", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "text": "test" + | } + | }, + | "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}" + |]""".stripMargin) + } + } + + @Test + def emailQueryShouldSupportTextFilterForMultipartMessage(server: GuiceJamesServer): Unit = { + server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB)) + + val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("a mail") + .setBody(MultipartBuilder.create() + .addTextPart("This is a test body", StandardCharsets.UTF_8) + .build()) + .build)) + .getMessageId + + server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("should not be found mail") + .setBody(MultipartBuilder.create() + .addTextPart("This is another body", StandardCharsets.UTF_8) + .build()) + .build)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "text": "test" + | } + | }, + | "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}" + |]""".stripMargin) + } + } + + @Test + def emailQueryFilterByTextShouldIgnoreMarkupsInHtmlBody(server: GuiceJamesServer): Unit = { + server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB)) + server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder + .of + .setSubject("A mail") + .setBody("<body><test>This is a html body<test></body>", "html", StandardCharsets.UTF_8) + .build)) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail"], + | "methodCalls": [[ + | "Email/query", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "filter": { + | "text": "test" + | } + | }, + | "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"""[]""".stripMargin) + } + } + private def sendMessageToBobInbox(server: GuiceJamesServer, message: Message, requestDate: Date): MessageId = { server.getProbe(classOf[MailboxProbeImpl]) .appendMessage(BOB.asString, MailboxPath.inbox(BOB), 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/MemoryEmailQueryMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java index 78b50d1..474f93a 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java @@ -26,6 +26,8 @@ import org.apache.james.JamesServerBuilder; import org.apache.james.JamesServerExtension; 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 MemoryEmailQueryMethodTest implements EmailQueryMethodContract { @@ -36,4 +38,8 @@ public class MemoryEmailQueryMethodTest implements EmailQueryMethodContract { .overrideWith(new TestJMAPServerModule())) .build(); + @Test + @Override + @Disabled("JAMES-3377 Not supported for in-memory test") + public void emailQueryFilterByTextShouldIgnoreMarkupsInHtmlBody(GuiceJamesServer server) {} } 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 30a1022..27e72c9 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 @@ -178,7 +178,7 @@ object MailboxFilter { case object Text extends QueryFilter { override def toQuery(builder: SearchQuery.Builder, request: EmailQueryRequest): Either[UnsupportedFilterException, SearchQuery.Builder] = request.filter.flatMap(_.text) match { - case Some(_) => Left(UnsupportedFilterException("text")) + case Some(text) => Right(builder.andCriteria(SearchQuery.textContains(text.value))) case None => Right(builder) } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
