blob: e029aa9299df0d351eb8fb67b981b8e993e2a2c4 [file] [log] [blame]
/****************************************************************
* 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.contract
import java.nio.charset.StandardCharsets
import java.time.Duration
import java.util.concurrent.TimeUnit
import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
import io.restassured.RestAssured._
import io.restassured.http.ContentType.JSON
import io.restassured.specification.RequestSpecification
import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
import org.apache.http.HttpStatus.SC_OK
import org.apache.james.GuiceJamesServer
import org.apache.james.core.Username
import org.apache.james.jmap.MessageIdProbe
import org.apache.james.jmap.core.ResponseObject.SESSION_STATE
import org.apache.james.jmap.http.UserCredential
import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ACCOUNT_ID, ANDRE, ANDRE_ACCOUNT_ID, ANDRE_IDENTITY_ID, ANDRE_PASSWORD, BOB, BOB_PASSWORD, CEDRIC, DAVID, DOMAIN, IDENTITY_ID, authScheme, baseRequestSpecBuilder}
import org.apache.james.jmap.rfc8621.contract.MDNSendMethodContract.TAG_MDN_MESSAGE_FORMAT
import org.apache.james.mailbox.MessageManager.AppendCommand
import org.apache.james.mailbox.model.{MailboxId, MailboxPath, MessageId, MultimailboxesSearchQuery, SearchQuery}
import org.apache.james.mime4j.codec.DecodeMonitor
import org.apache.james.mime4j.dom.{Message, Multipart}
import org.apache.james.mime4j.message.DefaultMessageBuilder
import org.apache.james.mime4j.stream.{MimeConfig, RawField}
import org.apache.james.modules.MailboxProbeImpl
import org.apache.james.utils.DataProbeImpl
import org.awaitility.Awaitility
import org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS
import org.awaitility.core.ConditionFactory
import org.junit.jupiter.api.{BeforeEach, Tag, Test}
import scala.jdk.CollectionConverters._
object MDNSendMethodContract {
val TAG_MDN_MESSAGE_FORMAT: "MDN_MESSAGE_FORMAT" = "MDN_MESSAGE_FORMAT"
}
trait MDNSendMethodContract {
private lazy val slowPacedPollInterval: Duration = ONE_HUNDRED_MILLISECONDS
private lazy val calmlyAwait: ConditionFactory = Awaitility.`with`
.pollInterval(slowPacedPollInterval)
.and.`with`.pollDelay(slowPacedPollInterval)
.await
private lazy val awaitAtMostTenSeconds: ConditionFactory = calmlyAwait.atMost(10, TimeUnit.SECONDS)
private def getFirstMessageInMailBox(guiceJamesServer: GuiceJamesServer, username: Username): Option[Message] = {
val searchByRFC822MessageId: MultimailboxesSearchQuery = MultimailboxesSearchQuery.from(SearchQuery.of(SearchQuery.all())).build
val defaultMessageBuilder: DefaultMessageBuilder = new DefaultMessageBuilder
defaultMessageBuilder.setMimeEntityConfig(MimeConfig.PERMISSIVE)
defaultMessageBuilder.setDecodeMonitor(DecodeMonitor.SILENT)
guiceJamesServer.getProbe(classOf[MailboxProbeImpl]).searchMessage(searchByRFC822MessageId, username.asString(), 100)
.asScala.headOption
.flatMap(messageId => guiceJamesServer.getProbe(classOf[MessageIdProbe]).getMessages(messageId, username).asScala.headOption)
.map(messageResult => defaultMessageBuilder.parseMessage(messageResult.getFullContent.getInputStream))
}
private def buildOriginalMessage(tag : String) :Message =
Message.Builder
.of
.setSubject(s"Subject of original message$tag")
.setSender(BOB.asString)
.setFrom(BOB.asString)
.setTo(ANDRE.asString)
.addField(new RawField("Disposition-Notification-To", s"Bob <${BOB.asString()}>"))
.setBody(s"Body of mail$tag, that mdn related", StandardCharsets.UTF_8)
.build
private def buildBOBRequestSpecification(server: GuiceJamesServer): RequestSpecification =
baseRequestSpecBuilder(server)
.setAuth(authScheme(UserCredential(BOB, BOB_PASSWORD)))
.addHeader(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.build
def randomMessageId: MessageId
@BeforeEach
def setUp(server: GuiceJamesServer): Unit = {
server.getProbe(classOf[DataProbeImpl])
.fluent()
.addDomain(DOMAIN.asString())
.addUser(BOB.asString(), BOB_PASSWORD)
.addUser(ANDRE.asString, ANDRE_PASSWORD)
.addUser(DAVID.asString, DAVID.asString())
requestSpecification = baseRequestSpecBuilder(server)
.setAuth(authScheme(UserCredential(ANDRE, ANDRE_PASSWORD)))
.addHeader(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.build()
}
@Test
def mdnSendShouldBeSuccessAndSendMailSuccessfully(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
val bobInboxId: MailboxId = mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| },
| "extensionFields": {
| "X-EXTENSION-EXAMPLE": "example.com"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.whenIgnoringPaths("methodResponses[1][1].newState",
"methodResponses[1][1].oldState")
.isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "sent": {
| "k1546": {
| "finalRecipient": "rfc822; ${ANDRE.asString}",
| "includeOriginalMessage": false,
| "originalRecipient": "rfc822; ${ANDRE.asString()}"
| }
| }
| },
| "c1"
| ],
| [
| "Email/set",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "oldState": "23",
| "newState": "42",
| "updated": {
| "${relatedEmailId.serialize()}": null
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin)
val requestQueryMDNMessage: String =
s"""{
| "using": ["urn:ietf:params:jmap:core","urn:ietf:params:jmap:mail"],
| "methodCalls": [[
| "Email/query",
| {
| "accountId": "$ACCOUNT_ID",
| "filter": {"inMailbox": "${bobInboxId.serialize}"}
| },
| "c1"]]
|}""".stripMargin
awaitAtMostTenSeconds.untilAsserted { () =>
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(requestQueryMDNMessage)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.inPath("methodResponses[0][1].ids")
.isArray
.hasSize(1)
}
}
@Test
def mdnSendShouldBeSuccessWhenRequestAssignFinalRecipient(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
server.getProbe(classOf[DataProbeImpl]).addUserAliasMapping("david", "domain.tld", "andre@domain.tld")
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "finalRecipient": "rfc822; ${ANDRE.asString()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.whenIgnoringPaths("methodResponses[1][1].newState",
"methodResponses[1][1].oldState")
.isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "sent": {
| "k1546": {
| "includeOriginalMessage": false,
| "originalRecipient": "rfc822; ${ANDRE.asString()}"
| }
| }
| },
| "c1"
| ],
| [
| "Email/set",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "oldState": "23",
| "newState": "42",
| "updated": {
| "${relatedEmailId.serialize()}": null
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenDispositionPropertyIsInvalid(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "disposition": {
| "actionMode": "invalidAction",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath(s"methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "invalidArguments",
| "description": "Disposition \"ActionMode\" is invalid.",
| "properties":["disposition"]
| }
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenFinalRecipientIsInvalid(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "finalRecipient" : "invalid",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath(s"methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "invalidArguments",
| "description": "FinalRecipient can't be parse.",
| "properties": [
| "finalRecipient"
| ]
| }
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenIdentityIsNotAllowedToUseFinalRecipient(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "finalRecipient" : "rfc822; ${CEDRIC.asString}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath(s"methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "forbiddenFrom",
| "description": "The user is not allowed to use the given \"finalRecipient\" property"
| }
|}""".stripMargin)
}
@Test
def implicitEmailSetShouldNotBeAttemptedWhenMDNIsNotSent(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "finalRecipient" : "invalid",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath("methodResponses[1]")
.isAbsent()
}
@Test
def implicitEmailSetShouldNotBeAttemptedWhenOnSuccessUpdateEmailIsNull(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.whenIgnoringPaths("methodResponses[1][1].newState",
"methodResponses[1][1].oldState")
.isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "sent": {
| "k1546": {
| "finalRecipient": "rfc822; ${ANDRE.asString}",
| "includeOriginalMessage": false,
| "originalRecipient": "rfc822; ${ANDRE.asString}"
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Test
def mdnSendShouldAcceptSeveralMDNObjects(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId1: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val relatedEmailId2: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("2")))
.getMessageId
val relatedEmailId3: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("3")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId1.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| },
| "k1547": {
| "forEmailId": "${relatedEmailId2.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| },
| "k1548": {
| "forEmailId": "${relatedEmailId3.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| },
| "#k1547": {
| "keywords/$$mdnsent": true
| },
| "#k1548": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.whenIgnoringPaths("methodResponses[1][1].newState",
"methodResponses[1][1].oldState")
.isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "sent": {
| "k1546": {
| "subject": "[Received] Subject of original message1",
| "textBody": "The email has been displayed on your recipient's computer",
| "originalRecipient": "rfc822; ${ANDRE.asString()}",
| "finalRecipient": "rfc822; ${ANDRE.asString()}",
| "includeOriginalMessage": false
| },
| "k1547": {
| "subject": "[Received] Subject of original message2",
| "textBody": "The email has been displayed on your recipient's computer",
| "originalRecipient": "rfc822; ${ANDRE.asString()}",
| "finalRecipient": "rfc822; ${ANDRE.asString()}",
| "includeOriginalMessage": false
| },
| "k1548": {
| "subject": "[Received] Subject of original message3",
| "textBody": "The email has been displayed on your recipient's computer",
| "originalRecipient": "rfc822; ${ANDRE.asString()}",
| "finalRecipient": "rfc822; ${ANDRE.asString()}",
| "includeOriginalMessage": false
| }
| }
| },
| "c1"
| ],
| [
| "Email/set",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "oldState": "3be4a1bc-0b41-4e33-aaf0-585e567a5af5",
| "newState": "3e1d5c70-9ca4-4c02-a35c-f54a51d253e3",
| "updated": {
| "${relatedEmailId1.serialize()}": null,
| "${relatedEmailId2.serialize()}": null,
| "${relatedEmailId3.serialize()}": null
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Test
def mdnSendMixValidAndNotFoundAndInvalid(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val validEmailId1: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val validEmailId2: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("2")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${validEmailId1.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| },
| "k1547": {
| "forEmailId": "${validEmailId2.serialize()}",
| "badProperty": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| },
| "k1548": {
| "forEmailId": "${randomMessageId.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| },
| "#k1547": {
| "keywords/$$mdnsent": true
| },
| "#k1548": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.whenIgnoringPaths("methodResponses[1][1].newState",
"methodResponses[1][1].oldState")
.isEqualTo(s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "sent": {
| "k1546": {
| "subject": "[Received] Subject of original message1",
| "textBody": "The email has been displayed on your recipient's computer",
| "originalRecipient": "rfc822; ${ANDRE.asString()}",
| "finalRecipient": "rfc822; ${ANDRE.asString()}",
| "includeOriginalMessage": false
| }
| },
| "notSent": {
| "k1547": {
| "type": "invalidArguments",
| "description": "Some unknown properties were specified",
| "properties": [
| "badProperty"
| ]
| },
| "k1548": {
| "type": "notFound",
| "description": "The reference \\"forEmailId\\" cannot be found."
| }
| }
| },
| "c1"
| ],
| [
| "Email/set",
| {
| "accountId": "1e8584548eca20f26faf6becc1704a0f352839f12c208a47fbd486d60f491f7c",
| "oldState": "eda83b09-6aca-4215-b493-2b4af19c50f0",
| "newState": "8bd671b2-e9fd-4ce3-b9b2-c3e1f35cc8ee",
| "updated": {
| "${validEmailId1.serialize()}": null
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenMDNHasAlreadyBeenSet(server: GuiceJamesServer): Unit = {
val path: MailboxPath = MailboxPath.inbox(BOB)
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(path)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(BOB.asString(), path, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ACCOUNT_ID",
| "identityId": "$IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.inPath(s"methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "mdnAlreadySent",
| "description": "The message has the $mdnsent keyword already set."
| }
|}""".stripMargin)
}
@Test
def mdnSendShouldReturnUnknownMethodWhenMissingOneCapability(server: GuiceJamesServer): Unit = {
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "ue150411c",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "Md45b47b4877521042cec0938",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response).isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [[
| "error",
| {
| "type": "unknownMethod",
| "description": "Missing capability(ies): urn:ietf:params:jmap:mdn"
| },
| "c1"]]
|}""".stripMargin)
}
@Test
def mdnSendShouldReturnUnknownMethodWhenMissingAllCapabilities(server: GuiceJamesServer): Unit = {
val request: String =
s"""{
| "using": [],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "ue150411c",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "Md45b47b4877521042cec0938",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response).isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [[
| "error",
| {
| "type": "unknownMethod",
| "description": "Missing capability(ies): urn:ietf:params:jmap:mdn, urn:ietf:params:jmap:mail, urn:ietf:params:jmap:core"
| },
| "c1"]]
|}""".stripMargin)
}
@Test
def mdnSendShouldReturnNotFoundWhenForEmailIdIsNotExist(server: GuiceJamesServer): Unit = {
val path: MailboxPath = MailboxPath.inbox(BOB)
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(path)
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ACCOUNT_ID",
| "identityId": "$IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${randomMessageId.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.inPath("methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "notFound",
| "description": "The reference \"forEmailId\" cannot be found."
| }
|}""".stripMargin)
}
@Test
def mdnSendShouldReturnNotFoundWhenMessageRelateHasNotDispositionNotificationTo(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val message: Message = Message.Builder
.of
.setSubject("test")
.setSender(BOB.asString)
.setFrom(BOB.asString)
.setTo(ANDRE.asString)
.setBody("Body of mail, that mdn related", StandardCharsets.UTF_8)
.build
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(message))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.inPath("methodResponses[0][1].notSent")
.isEqualTo("""{
| "k1546": {
| "type": "notFound",
| "description": "Invalid \"Disposition-Notification-To\" header field."
| }
|}""".stripMargin)
}
@Test
def mdnSendShouldReturnInvalidWhenIdentityDoesNotExist(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val message: Message = Message.Builder
.of
.setSubject("test")
.setSender(BOB.asString)
.setFrom(BOB.asString)
.setTo(ANDRE.asString)
.setBody("Body of mail, that mdn related", StandardCharsets.UTF_8)
.build
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(message))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "notFound",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.isEqualTo(s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "error",
| {
| "type": "invalidArguments",
| "description": "The IdentityId cannot be found"
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
val path: MailboxPath = MailboxPath.inbox(BOB)
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(path)
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "unknownAccountId",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response).isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [[
| "error",
| {
| "type": "accountNotFound"
| },
| "c1"
| ]]
|}""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenOnSuccessUpdateEmailMissesTheCreationIdSharp(server: GuiceJamesServer): Unit = {
val path: MailboxPath = MailboxPath.inbox(BOB)
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(path)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(BOB.asString(), path, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "notStored": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.inPath("methodResponses[0]")
.isEqualTo(
s"""[
| "error",
| {
| "type": "invalidArguments",
| "description": "notStored cannot be retrieved as storage for MDNSend is not yet implemented"
| },
| "c1"
|]""".stripMargin)
}
@Test
def mdnSendShouldBeFailWhenOnSuccessUpdateEmailDoesNotReferenceACreationWithinThisCall(server: GuiceJamesServer): Unit = {
val path: MailboxPath = MailboxPath.inbox(BOB)
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(path)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(BOB.asString(), path, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#notReference": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val response: String =
`given`(buildBOBRequestSpecification(server))
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(response)
.isEqualTo(
s"""{
| "sessionState": "${SESSION_STATE.value}",
| "methodResponses": [
| [
| "error",
| {
| "type": "invalidArguments",
| "description": "#notReference cannot be referenced in current method call"
| },
| "c1"
| ]
| ]
|}""".stripMargin)
}
@Tag(TAG_MDN_MESSAGE_FORMAT)
@Test
def mdnSendShouldReturnSubjectWhenRequestDoNotSet(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath("methodResponses[0][1].sent.k1546.subject")
.asString().isEqualTo("[Received] Subject of original message1")
}
@Tag(TAG_MDN_MESSAGE_FORMAT)
@Test
def mdnSendShouldReturnTextBodyWhenRequestDoNotSet(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath("methodResponses[0][1].sent.k1546.textBody")
.asString().isEqualTo("The email has been displayed on your recipient's computer")
}
@Tag(TAG_MDN_MESSAGE_FORMAT)
@Test
def mdnSendShouldReturnOriginalMessageIdWhenRelatedMessageHasMessageIDHeader(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(Message.Builder
.of
.setSubject(s"Subject of original message")
.setSender(BOB.asString)
.setFrom(BOB.asString)
.setTo(ANDRE.asString)
.addField(new RawField("Disposition-Notification-To", s"Bob <${BOB.asString()}>"))
.addField(new RawField("Message-Id", "<199509192301.23456@example.org>"))
.setBody(s"Body of mail, that mdn related", StandardCharsets.UTF_8)
.build
))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
val mdnSendResponse: String =
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
assertThatJson(mdnSendResponse)
.inPath("methodResponses[0][1].sent.k1546.originalMessageId")
.asString().isEqualTo("<199509192301.23456@example.org>")
}
@Tag(TAG_MDN_MESSAGE_FORMAT)
@Test
def mdnMessageShouldHasThirdBodyPartWhenIncludeOriginalMessageIsTrue(server: GuiceJamesServer): Unit = {
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val bobMailBoxPath: MailboxPath = MailboxPath.inbox(BOB)
mailboxProbe.createMailbox(bobMailBoxPath)
val andreMailBoxPath: MailboxPath = MailboxPath.inbox(ANDRE)
mailboxProbe.createMailbox(andreMailBoxPath)
val relatedEmailId: MessageId = mailboxProbe
.appendMessage(ANDRE.asString(), andreMailBoxPath, AppendCommand.builder()
.build(buildOriginalMessage("1")))
.getMessageId
val request: String =
s"""{
| "using": [
| "urn:ietf:params:jmap:core",
| "urn:ietf:params:jmap:mail",
| "urn:ietf:params:jmap:mdn"
| ],
| "methodCalls": [
| [
| "MDN/send",
| {
| "accountId": "$ANDRE_ACCOUNT_ID",
| "identityId": "$ANDRE_IDENTITY_ID",
| "send": {
| "k1546": {
| "forEmailId": "${relatedEmailId.serialize()}",
| "subject": "Read receipt for: World domination",
| "textBody": "This receipt shows that the email has been displayed on your recipient's computer. ",
| "reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
| "disposition": {
| "actionMode": "manual-action",
| "sendingMode": "mdn-sent-manually",
| "type": "displayed"
| },
| "includeOriginalMessage": true,
| "extensionFields": {
| "X-EXTENSION-EXAMPLE": "example.com"
| }
| }
| },
| "onSuccessUpdateEmail": {
| "#k1546": {
| "keywords/$$mdnsent": true
| }
| }
| },
| "c1"
| ]
| ]
|}""".stripMargin
`given`
.body(request)
.when
.post
.`then`
.statusCode(SC_OK)
.contentType(JSON)
.extract
.body
.asString
awaitAtMostTenSeconds.untilAsserted { () =>
val mdnBodyPartCounter = getFirstMessageInMailBox(server, BOB)
.filter(msg => msg.isMultipart)
.map(msg => msg.getBody.asInstanceOf[Multipart].getBodyParts)
assert(mdnBodyPartCounter.isDefined && mdnBodyPartCounter.get.size == 3)
}
}
}