blob: 1cadec1dbffa64ad8ebfc6c859aa3cc0e40519f7 [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.draft.methods;
import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import jakarta.inject.Inject;
import org.apache.james.jmap.draft.exceptions.MailboxHasChildException;
import org.apache.james.jmap.draft.exceptions.SystemMailboxNotUpdatableException;
import org.apache.james.jmap.draft.model.MailboxFactory;
import org.apache.james.jmap.draft.model.SetError;
import org.apache.james.jmap.draft.model.SetMailboxesRequest;
import org.apache.james.jmap.draft.model.SetMailboxesResponse;
import org.apache.james.jmap.draft.model.SetMailboxesResponse.Builder;
import org.apache.james.jmap.draft.model.mailbox.Mailbox;
import org.apache.james.jmap.draft.utils.MailboxUtils;
import org.apache.james.jmap.draft.utils.SortingHierarchicalCollections;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.Role;
import org.apache.james.mailbox.SubscriptionManager;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.TooLongMailboxNameException;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.metrics.api.MetricFactory;
import org.apache.james.metrics.api.TimeMetric;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
public class SetMailboxesDestructionProcessor implements SetMailboxesProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(SetMailboxesDestructionProcessor.class);
private final MailboxManager mailboxManager;
private final SubscriptionManager subscriptionManager;
private final SortingHierarchicalCollections<Map.Entry<MailboxId, Mailbox>, MailboxId> sortingHierarchicalCollections;
private final MailboxUtils mailboxUtils;
private final MailboxFactory mailboxFactory;
private final MetricFactory metricFactory;
@Inject
@VisibleForTesting
SetMailboxesDestructionProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, MailboxUtils mailboxUtils, MailboxFactory mailboxFactory, MetricFactory metricFactory) {
this.mailboxManager = mailboxManager;
this.subscriptionManager = subscriptionManager;
this.metricFactory = metricFactory;
this.sortingHierarchicalCollections =
new SortingHierarchicalCollections<>(
Entry::getKey,
x -> x.getValue().getParentId());
this.mailboxUtils = mailboxUtils;
this.mailboxFactory = mailboxFactory;
}
@Override
public SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession) {
TimeMetric timeMetric = metricFactory.timer(JMAP_PREFIX + "SetMailboxesDestructionProcessor");
ImmutableMap<MailboxId, Mailbox> idToMailbox = mapDestroyRequests(request, mailboxSession);
SetMailboxesResponse.Builder builder = SetMailboxesResponse.builder();
sortingHierarchicalCollections.sortFromLeafToRoot(idToMailbox.entrySet())
.forEach(entry -> destroyMailbox(entry, mailboxSession, builder));
notDestroyedRequests(request, idToMailbox, builder);
timeMetric.stopAndPublish();
return builder.build();
}
private ImmutableMap<MailboxId, Mailbox> mapDestroyRequests(SetMailboxesRequest request, MailboxSession mailboxSession) {
ImmutableMap.Builder<MailboxId, Mailbox> idToMailboxBuilder = ImmutableMap.builder();
request.getDestroy().stream()
.map(id -> mailboxFactory.builder()
.id(id)
.session(mailboxSession)
.build()
.blockOptional())
.flatMap(Optional::stream)
.forEach(mailbox -> idToMailboxBuilder.put(mailbox.getId(), mailbox));
return idToMailboxBuilder.build();
}
private void notDestroyedRequests(SetMailboxesRequest request, ImmutableMap<MailboxId, Mailbox> idToMailbox, SetMailboxesResponse.Builder builder) {
request.getDestroy().stream()
.filter(id -> !idToMailbox.containsKey(id))
.forEach(id -> notDestroy(id, builder));
}
private void destroyMailbox(Entry<MailboxId, Mailbox> entry, MailboxSession mailboxSession, SetMailboxesResponse.Builder builder) {
try {
Mailbox mailbox = entry.getValue();
preconditions(mailbox, mailboxSession);
MailboxPath deletedMailbox = mailboxManager.deleteMailbox(mailbox.getId(), mailboxSession).generateAssociatedPath();
subscriptionManager.unsubscribe(mailboxSession, deletedMailbox);
builder.destroyed(entry.getKey());
} catch (MailboxHasChildException e) {
builder.notDestroyed(entry.getKey(), SetError.builder()
.type(SetError.Type.MAILBOX_HAS_CHILD)
.description(String.format("The mailbox '%s' has a child.", entry.getKey().serialize()))
.build());
} catch (SystemMailboxNotUpdatableException e) {
builder.notDestroyed(entry.getKey(), SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description(String.format("The mailbox '%s' is a system mailbox.", entry.getKey().serialize()))
.build());
} catch (TooLongMailboxNameException e) {
builder.notDestroyed(entry.getKey(), SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description("The mailbox name length is too long")
.build());
} catch (MailboxException e) {
String message = String.format("An error occurred when deleting the mailbox '%s'", entry.getKey().serialize());
LOGGER.error(message, e);
builder.notDestroyed(entry.getKey(), SetError.builder()
.type(SetError.Type.ERROR)
.description(message)
.build());
}
}
private void preconditions(Mailbox mailbox, MailboxSession mailboxSession) throws MailboxHasChildException, SystemMailboxNotUpdatableException, MailboxException {
checkForChild(mailbox.getId(), mailboxSession);
checkRole(mailbox.getRole());
}
private void checkForChild(MailboxId id, MailboxSession mailboxSession) throws MailboxHasChildException, MailboxException {
if (mailboxUtils.hasChildren(id, mailboxSession)) {
throw new MailboxHasChildException();
}
}
private void checkRole(Optional<Role> role) throws SystemMailboxNotUpdatableException {
if (role.map(Role::isSystemRole).orElse(false)) {
throw new SystemMailboxNotUpdatableException();
}
}
private void notDestroy(MailboxId id, Builder builder) {
builder.notDestroyed(id, SetError.builder()
.type(SetError.Type.NOT_FOUND)
.description(String.format("The mailbox '%s' was not found.", id.serialize()))
.build());
}
}