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
The following commit(s) were added to refs/heads/master by this push: new 1f2f818c23 JAMES-4096 Rename INBOX (#2549) 1f2f818c23 is described below commit 1f2f818c23b7ce0b676c582d8e355aabcae6978c Author: hungphan227 <45198168+hungphan...@users.noreply.github.com> AuthorDate: Fri Dec 6 23:49:39 2024 +0700 JAMES-4096 Rename INBOX (#2549) Co-authored-by: hung phan <hp...@linagora.com> --- .../james/mailbox/store/StoreMailboxManager.java | 41 ++-- .../apache/james/mpt/imapmailbox/suite/Rename.java | 6 + .../org/apache/james/imap/scripts/RenameInbox.test | 75 ++++++ .../contract/MailboxSetMethodContract.scala | 269 ++++++++++++++++++++- .../jmap/method/MailboxSetUpdatePerformer.scala | 71 +++--- 5 files changed, 417 insertions(+), 45 deletions(-) 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 86972fbd2a..4eda433a06 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 @@ -37,6 +37,7 @@ import org.apache.james.core.Username; import org.apache.james.core.quota.QuotaCountUsage; import org.apache.james.core.quota.QuotaSizeUsage; import org.apache.james.events.EventBus; +import org.apache.james.mailbox.DefaultMailboxes; import org.apache.james.mailbox.MailboxAnnotationManager; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxPathLocker; @@ -689,22 +690,7 @@ public class StoreMailboxManager implements MailboxManager { resultBuilder.add(new MailboxRenamedResult(mailboxId, from, newMailboxPath)); return mailboxId; }) - .then(Mono.from(locker.executeReactiveWithLockReactive(from, mapper.findMailboxWithPathLike(query) - .concatMap(sub -> { - String subOriginalName = sub.getName(); - String subNewName = newMailboxPath.getName() + subOriginalName.substring(from.getName().length()); - MailboxPath fromPath = new MailboxPath(from, subOriginalName); - sub.setName(subNewName); - sub.setUser(toSession.getUser()); - return mapper.rename(sub) - .map(mailboxId -> { - resultBuilder.add(new MailboxRenamedResult(sub.getMailboxId(), fromPath, sub.generateAssociatedPath())); - return mailboxId; - }) - .retryWhen(Retry.backoff(5, Duration.ofMillis(10))) - .then(Mono.fromRunnable(() -> LOGGER.debug("Rename mailbox sub-mailbox {} to {}", subOriginalName, subNewName))); - }, LOW_CONCURRENCY) - .then(), MailboxPathLocker.LockType.Write))) + .then(Mono.from(renameSubMailboxes(newMailboxPath, toSession, mapper, from, query, resultBuilder))) .then(Mono.defer(() -> Flux.fromIterable(resultBuilder.build()) .concatMap(result -> eventBus.dispatch(EventFactory.mailboxRenamed() .randomEventId() @@ -718,6 +704,29 @@ public class StoreMailboxManager implements MailboxManager { .then(Mono.fromCallable(resultBuilder::build)); } + private Publisher<Void> renameSubMailboxes(MailboxPath newMailboxPath, MailboxSession toSession, MailboxMapper mapper, + MailboxPath from, MailboxQuery.UserBound query, ImmutableList.Builder<MailboxRenamedResult> resultBuilder) { + if (DefaultMailboxes.INBOX.equalsIgnoreCase(from.getName())) { + return Mono.empty(); + } + return locker.executeReactiveWithLockReactive(from, mapper.findMailboxWithPathLike(query) + .concatMap(sub -> { + String subOriginalName = sub.getName(); + String subNewName = newMailboxPath.getName() + subOriginalName.substring(from.getName().length()); + MailboxPath fromPath = new MailboxPath(from, subOriginalName); + sub.setName(subNewName); + sub.setUser(toSession.getUser()); + return mapper.rename(sub) + .map(mailboxId -> { + resultBuilder.add(new MailboxRenamedResult(sub.getMailboxId(), fromPath, sub.generateAssociatedPath())); + return mailboxId; + }) + .retryWhen(Retry.backoff(5, Duration.ofMillis(10))) + .then(Mono.fromRunnable(() -> LOGGER.debug("Rename mailbox sub-mailbox {} to {}", subOriginalName, subNewName))); + }, LOW_CONCURRENCY) + .then(), MailboxPathLocker.LockType.Write); + } + @Override public List<MessageRange> copyMessages(MessageRange set, MailboxPath from, MailboxPath to, MailboxSession session) throws MailboxException { return MailboxReactorUtils.block(copyMessagesReactive(set, from, to, session).collectList()); diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/Rename.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/Rename.java index 6ab8d07750..e912e604a5 100644 --- a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/Rename.java +++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/Rename.java @@ -108,4 +108,10 @@ public abstract class Rename implements ImapTestConstants { .withLocale(Locale.KOREA) .run("RenameSelected"); } + + @Test + public void testRenameInbox() throws Exception { + simpleScriptedTestProtocol + .run("RenameInbox"); + } } diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/RenameInbox.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/RenameInbox.test new file mode 100644 index 0000000000..50e7998144 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/RenameInbox.test @@ -0,0 +1,75 @@ +################################################################ +# 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. # +################################################################ + +C: A0 APPEND INBOX {185+} +C: From: Timothy Tayler <timo...@example.org> +C: To: Samual Smith <sam...@example.org> +C: Date: Thu, 14 Feb 2008 12:00:00 +0000 (GMT) +C: Subject: A Simple Email +C: +C: This is a very simple email. +C: +S: A0 OK (\[.+\] )?APPEND completed\. + +C: A1 CREATE INBOX.child +S: A1 OK \[MAILBOXID \(.+\)\] CREATE completed\. + +C: A2 RENAME INBOX testmailbox +S: A2 OK RENAME completed\. + +C: A3 SELECT testmailbox +S: \* OK \[MAILBOXID \(.+\)\] Ok +S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\) +S: \* 1 EXISTS +S: \* 1 RECENT +S: \* OK \[UIDVALIDITY \d+\].* +S: \* OK \[UNSEEN 1].* +S: \* OK \[PERMANENTFLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\\Seen( \\\*)?\)\].* +S: \* OK \[HIGHESTMODSEQ \d+\].* +S: \* OK \[UIDNEXT 2\].* +S: A3 OK \[READ-WRITE\] SELECT completed\. + +C: A4 SELECT INBOX +S: \* OK \[MAILBOXID \(.+\)\] Ok +S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\) +S: \* 0 EXISTS +S: \* 0 RECENT +S: \* OK \[UIDVALIDITY \d+\].* +S: \* OK \[PERMANENTFLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\\Seen( \\\*)?\)\].* +S: \* OK \[HIGHESTMODSEQ \d+\].* +S: \* OK \[UIDNEXT 1\].* +S: A4 OK \[READ-WRITE\] SELECT completed\. + +C: A5 SELECT INBOX.child +S: \* OK \[MAILBOXID \(.+\)\] Ok +S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\) +S: \* 0 EXISTS +S: \* 0 RECENT +S: \* OK \[UIDVALIDITY \d+\].* +S: \* OK \[PERMANENTFLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\\Seen( \\\*)?\)\].* +S: \* OK \[HIGHESTMODSEQ \d+\].* +S: \* OK \[UIDNEXT 1\].* +S: A5 OK \[READ-WRITE\] SELECT completed\. + +C: A6 LIST "" "*" +S: \* LIST \(\\HasChildren\) "\." "INBOX" +S: \* LIST \(\\HasNoChildren\) "\." "INBOX\.child" +S: \* LIST \(\\HasNoChildren\) "\." "selected" +S: \* LIST \(\\HasNoChildren\) "\." "testmailbox" +S: A6 OK LIST completed\. 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 f6d96190c9..9263595755 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 @@ -37,9 +37,10 @@ import org.apache.james.jmap.http.UserCredential import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ACCOUNT_ID, ANDRE, BOB, BOB_PASSWORD, CEDRIC, DAVID, DOMAIN, authScheme, baseRequestSpecBuilder} import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags import org.apache.james.jmap.{JmapGuiceProbe, MessageIdProbe} +import org.apache.james.mailbox.DefaultMailboxes import org.apache.james.mailbox.MessageManager.AppendCommand import org.apache.james.mailbox.model.MailboxACL.{EntryKey, Right} -import org.apache.james.mailbox.model.{MailboxACL, MailboxId, MailboxPath} +import org.apache.james.mailbox.model.{MailboxACL, MailboxConstants, MailboxId, MailboxPath} import org.apache.james.mime4j.dom.Message import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl} import org.apache.james.util.concurrency.ConcurrentTestRunner @@ -3802,7 +3803,7 @@ trait MailboxSetMethodContract { @Test def updateShouldNotRenameSystemMailboxes(server: GuiceJamesServer): Unit = { val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]) - .createMailbox(MailboxPath.forUser(BOB, "INBOX")) + .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.DRAFTS)) val request = s""" |{ @@ -3854,6 +3855,270 @@ trait MailboxSetMethodContract { |}""".stripMargin) } + @Test + def updateShouldRenameInbox(server: GuiceJamesServer): Unit = { + val bobPath = MailboxPath.inbox(BOB) + val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath) + server.getProbe(classOf[MailboxProbeImpl]).appendMessage(BOB.asString(), bobPath, + AppendCommand.from(Message.Builder.of.setSubject("test").setBody("testmail", StandardCharsets.UTF_8).build)) + + val request = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | ["Mailbox/set", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "update": { + | "${mailboxId.serialize}" : { + | "name": "newName" + | } + | } + | }, + | "c2"], + | ["Mailbox/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "properties": ["id", "name", "totalEmails"], + | "ids": ["${mailboxId.serialize}"] + | }, + | "c2"] + | ] + |} + |""".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) + .whenIgnoringPaths("methodResponses[1][1].state", "methodResponses[0][1].newState", "methodResponses[0][1].oldState") + .isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [ + | ["Mailbox/set", { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "updated": { + | "${mailboxId.serialize}": {} + | } + | }, "c2"], + | ["Mailbox/get", { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "state": "${INSTANCE.value}", + | "list": [{ + | "id": "${mailboxId.serialize}", + | "name": "newName", + | "totalEmails": 1 + | }], + | "notFound": [] + | }, "c2"] + | ] + |}""".stripMargin) + } + + @Test + def renameInboxShouldCreateNewInbox(server: GuiceJamesServer): Unit = { + val bobPath = MailboxPath.inbox(BOB) + val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath) + server.getProbe(classOf[MailboxProbeImpl]).appendMessage(BOB.asString(), bobPath, + AppendCommand.from(Message.Builder.of.setSubject("test").setBody("testmail", StandardCharsets.UTF_8).build)) + + val request = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | ["Mailbox/set", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "update": { + | "${mailboxId.serialize}" : { + | "name": "newName" + | } + | } + | }, + | "c2"], + | ["Mailbox/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "properties": ["id", "name"], + | "ids": ["${mailboxId.serialize}"] + | }, + | "c2"] + | ] + |} + |""".stripMargin + + `given` + .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .body(request) + .when + .post + .`then` + .log().ifValidationFails() + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + val newInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).getMailboxId("#private", BOB.asString(), MailboxConstants.INBOX) + + val request2 = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | ["Mailbox/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "properties": ["id", "name", "totalEmails"], + | "ids": ["${newInboxId.serialize}"] + | }, + | "c2"] + | ] + |} + |""".stripMargin + + val response2 = `given` + .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .body(request2) + .when + .post + .`then` + .log().ifValidationFails() + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + assertThatJson(response2) + .whenIgnoringPaths("methodResponses[0][1].state", "methodResponses[0][1].newState", "methodResponses[0][1].oldState") + .isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [ + | ["Mailbox/get", { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "state": "${INSTANCE.value}", + | "list": [{ + | "id": "${newInboxId.serialize}", + | "name": "INBOX", + | "totalEmails": 0 + | }], + | "notFound": [] + | }, "c2"] + | ] + |}""".stripMargin) + } + + @Test + def renameInboxShouldNotAffectSubMailboxes(server: GuiceJamesServer): Unit = { + val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.forUser(BOB, "INBOX")) + val subInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.forUser(BOB, "INBOX.subInbox")) + + val request = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | ["Mailbox/set", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "update": { + | "${mailboxId.serialize}" : { + | "name": "newName" + | } + | } + | }, + | "c2"], + | ["Mailbox/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "properties": ["id", "name"], + | "ids": ["${mailboxId.serialize}"] + | }, + | "c2"] + | ] + |} + |""".stripMargin + + `given` + .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .body(request) + .when + .post + .`then` + .log().ifValidationFails() + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + val newInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).getMailboxId("#private", BOB.asString(), MailboxConstants.INBOX) + + val request2 = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | ["Mailbox/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "properties": ["id", "name", "parentId"], + | "ids": ["${subInboxId.serialize}"] + | }, + | "c2"] + | ] + |} + |""".stripMargin + + val response2 = `given` + .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .body(request2) + .when + .post + .`then` + .log().ifValidationFails() + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + assertThatJson(response2) + .whenIgnoringPaths("methodResponses[0][1].state", "methodResponses[0][1].newState", "methodResponses[0][1].oldState") + .isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [ + | ["Mailbox/get", { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "state": "${INSTANCE.value}", + | "list": [{ + | "id": "${subInboxId.serialize}", + | "name": "subInbox", + | "parentId": "${newInboxId.serialize}" + | }], + | "notFound": [] + | }, "c2"] + | ] + |}""".stripMargin) + } + @Test def destroyShouldNotRemoveSystemMailboxes(server: GuiceJamesServer): Unit = { val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala index 615d96b193..7824aff7d2 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala @@ -32,7 +32,7 @@ import org.apache.james.mailbox.MailboxManager.{MailboxSearchFetchType, RenameOp import org.apache.james.mailbox.exception.{DifferentDomainException, InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException} import org.apache.james.mailbox.model.search.{MailboxQuery, PrefixedWildcard} import org.apache.james.mailbox.model.{MailboxId, MailboxPath} -import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager} +import org.apache.james.mailbox.{DefaultMailboxes, MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager} import org.apache.james.util.{AuditTrail, ReactorUtils} import org.slf4j.LoggerFactory import reactor.core.scala.publisher.{SFlux, SMono} @@ -164,31 +164,33 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = { if (validatedPatch.shouldUpdateMailboxPath) { - SMono.fromCallable[MailboxUpdateResult](() => { - try { - val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession) - 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)) - .apply(oldPath) - if (!oldPath.equals(newPath)) { - mailboxManager.renameMailbox(mailboxId, - newPath, - RenameOption.RENAME_SUBSCRIPTIONS, - mailboxSession) - } - MailboxUpdateSuccess(mailboxId) - } catch { - case e: Exception => MailboxUpdateFailure(unparsedMailboxId, e, Some(validatedPatch)) - } - }) - .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) + SMono(mailboxManager.getMailboxReactive(mailboxId, mailboxSession)) + .flatMap(mailbox => + SMono.fromCallable[MailboxUpdateResult](() => { + try { + if (isASystemMailbox(mailbox) && !DefaultMailboxes.INBOX.equalsIgnoreCase(mailbox.getMailboxPath.getName)) { + 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)) + .apply(oldPath) + if (!oldPath.equals(newPath)) { + mailboxManager.renameMailbox(mailboxId, + newPath, + RenameOption.RENAME_SUBSCRIPTIONS, + mailboxSession) + } + MailboxUpdateSuccess(mailboxId) + } catch { + case e: Exception => MailboxUpdateFailure(unparsedMailboxId, e, Some(validatedPatch)) + } + }).subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) + .flatMap(updateResult => createInboxIfNeeded(mailbox.getMailboxPath, mailboxSession) + .`then`(SMono.just(updateResult)))) } else { SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId)) } @@ -269,6 +271,21 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, } - private def isASystemMailbox(mailbox: MessageManager): Boolean = Role.from(mailbox.getMailboxPath.getName).isPresent + private def createInboxIfNeeded(existingPath: MailboxPath, session: MailboxSession): SMono[Unit] = { + if (!existingPath.getName.equalsIgnoreCase(DefaultMailboxes.INBOX)) { + return SMono.empty + } + SMono(mailboxManager.mailboxExists(existingPath, session)).flatMap(exists => createInbox(exists, existingPath, session)) + } + + private def createInbox(exists: Boolean, existingPath: MailboxPath, session: MailboxSession): SMono[Unit] = { + if (exists) { + return SMono.empty + } + SMono(mailboxManager.createMailboxReactive(existingPath, session)) + .`then`() + } + private def isASystemMailbox(mailbox: MessageManager): Boolean = + Role.from(mailbox.getMailboxPath.getName).isPresent } --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org