blob: 57971dba33dd16c74eddf9408495d2796f42550a [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.api.change;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.apache.james.jmap.api.model.AccountId;
import org.apache.james.mailbox.events.MailboxEvents;
import org.apache.james.mailbox.events.MailboxEvents.MailboxACLUpdated;
import org.apache.james.mailbox.events.MailboxEvents.MailboxAdded;
import org.apache.james.mailbox.events.MailboxEvents.MailboxDeletion;
import org.apache.james.mailbox.events.MailboxEvents.MailboxRenamed;
import org.apache.james.mailbox.model.MailboxACL;
import org.apache.james.mailbox.model.MailboxId;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
public class MailboxChange implements JmapChange {
public static class Builder {
@FunctionalInterface
public interface RequiredAccountId {
RequiredState accountId(AccountId accountId);
}
@FunctionalInterface
public interface RequiredState {
RequiredDate state(State state);
}
@FunctionalInterface
public interface RequiredDate {
RequiredIsCountChange date(ZonedDateTime date);
}
@FunctionalInterface
public interface RequiredIsCountChange {
Builder isCountChange(boolean isCountChange);
}
private final AccountId accountId;
private final State state;
private final ZonedDateTime date;
private boolean delegated;
private final boolean isCountChange;
private Optional<List<MailboxId>> created;
private Optional<List<MailboxId>> updated;
private Optional<List<MailboxId>> destroyed;
private Builder(AccountId accountId, State state, ZonedDateTime date, boolean isCountChange) {
Preconditions.checkNotNull(accountId, "'accountId' cannot be null");
Preconditions.checkNotNull(state, "'state' cannot be null");
Preconditions.checkNotNull(date, "'date' cannot be null");
this.accountId = accountId;
this.state = state;
this.date = date;
this.isCountChange = isCountChange;
this.created = Optional.empty();
this.updated = Optional.empty();
this.destroyed = Optional.empty();
}
public Builder delegated() {
return delegated(true);
}
public Builder delegated(boolean delegated) {
this.delegated = delegated;
return this;
}
public Builder created(List<MailboxId> created) {
this.created = Optional.of(created);
return this;
}
public Builder updated(List<MailboxId> updated) {
this.updated = Optional.of(updated);
return this;
}
public Builder destroyed(List<MailboxId> destroyed) {
this.destroyed = Optional.of(destroyed);
return this;
}
public MailboxChange build() {
return new MailboxChange(accountId, state, date, delegated, isCountChange, created.orElse(ImmutableList.of()), updated.orElse(ImmutableList.of()), destroyed.orElse(ImmutableList.of()));
}
}
public static Builder.RequiredAccountId builder() {
return accountId -> state -> date -> isCountChange -> new Builder(accountId, state, date, isCountChange);
}
public static class Factory {
private final State.Factory stateFactory;
@Inject
public Factory(State.Factory stateFactory) {
this.stateFactory = stateFactory;
}
public JmapChange fromMailboxAdded(MailboxAdded mailboxAdded, ZonedDateTime now) {
return MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxAdded.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.created(ImmutableList.of(mailboxAdded.getMailboxId()))
.build();
}
public List<JmapChange> fromMailboxRenamed(MailboxRenamed mailboxRenamed, ZonedDateTime now, List<AccountId> sharees) {
MailboxChange ownerChange = MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxRenamed.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxRenamed.getMailboxId()))
.build();
Stream<MailboxChange> shareeChanges = sharees.stream()
.map(shareeId -> MailboxChange.builder()
.accountId(shareeId)
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxRenamed.getMailboxId()))
.delegated()
.build());
return Stream.concat(Stream.of(ownerChange), shareeChanges)
.collect(ImmutableList.toImmutableList());
}
public JmapChange fromMailboxSubscribed(MailboxEvents.MailboxSubscribedEvent mailboxSubscribedEvent, ZonedDateTime now) {
return MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxSubscribedEvent.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxSubscribedEvent.getMailboxId()))
.build();
}
public JmapChange fromMailboxUnSubscribed(MailboxEvents.MailboxUnsubscribedEvent mailboxUnsubscribedEvent, ZonedDateTime now) {
return MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxUnsubscribedEvent.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxUnsubscribedEvent.getMailboxId()))
.build();
}
public List<JmapChange> fromMailboxACLUpdated(MailboxACLUpdated mailboxACLUpdated, ZonedDateTime now, List<AccountId> sharees) {
MailboxChange ownerChange = MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxACLUpdated.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxACLUpdated.getMailboxId()))
.build();
Stream<MailboxChange> shareeChanges = sharees.stream()
.map(shareeId -> MailboxChange.builder()
.accountId(shareeId)
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxACLUpdated.getMailboxId()))
.delegated()
.build());
Stream<MailboxChange> deletionChanges = mailboxACLUpdated.getAclDiff()
.removedEntries()
.filter(entry -> entry.getKey().getNameType().equals(MailboxACL.NameType.user))
.filter(entry -> !entry.getKey().isNegative())
.map(entry -> MailboxChange.builder()
.accountId(AccountId.fromString(entry.getKey().getName()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.updated(ImmutableList.of(mailboxACLUpdated.getMailboxId()))
.delegated()
.build());
return Stream.of(Stream.of(ownerChange), shareeChanges, deletionChanges)
.flatMap(e -> e)
.collect(ImmutableList.toImmutableList());
}
public List<JmapChange> fromMailboxDeletion(MailboxDeletion mailboxDeletion, ZonedDateTime now) {
MailboxChange ownerChange = MailboxChange.builder()
.accountId(AccountId.fromUsername(mailboxDeletion.getUsername()))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.destroyed(ImmutableList.of(mailboxDeletion.getMailboxId()))
.build();
Stream<MailboxChange> shareeChanges = mailboxDeletion.getMailboxACL()
.getEntries().keySet()
.stream()
.filter(rfc4314Rights -> !rfc4314Rights.isNegative())
.filter(rfc4314Rights -> rfc4314Rights.getNameType().equals(MailboxACL.NameType.user))
.map(MailboxACL.EntryKey::getName)
.map(name -> MailboxChange.builder()
.accountId(AccountId.fromString(name))
.state(stateFactory.generate())
.date(now)
.isCountChange(false)
.destroyed(ImmutableList.of(mailboxDeletion.getMailboxId()))
.delegated()
.build());
return Stream.concat(Stream.of(ownerChange), shareeChanges)
.collect(ImmutableList.toImmutableList());
}
}
private final AccountId accountId;
private final State state;
private final ZonedDateTime date;
private final boolean delegated;
private final boolean isCountChange;
private final List<MailboxId> created;
private final List<MailboxId> updated;
private final List<MailboxId> destroyed;
private MailboxChange(AccountId accountId, State state, ZonedDateTime date, boolean delegated, boolean isCountChange, List<MailboxId> created, List<MailboxId> updated, List<MailboxId> destroyed) {
this.accountId = accountId;
this.state = state;
this.date = date;
this.delegated = delegated;
this.isCountChange = isCountChange;
this.created = created;
this.updated = updated;
this.destroyed = destroyed;
}
public AccountId getAccountId() {
return accountId;
}
public State getState() {
return state;
}
public ZonedDateTime getDate() {
return date;
}
public boolean isDelegated() {
return delegated;
}
public boolean isCountChange() {
return isCountChange;
}
public List<MailboxId> getCreated() {
return created;
}
public List<MailboxId> getUpdated() {
return updated;
}
public List<MailboxId> getDestroyed() {
return destroyed;
}
@Override
public final boolean equals(Object o) {
if (o instanceof MailboxChange) {
MailboxChange that = (MailboxChange) o;
return Objects.equals(accountId, that.accountId)
&& Objects.equals(state, that.state)
&& Objects.equals(date, that.date)
&& Objects.equals(isCountChange, that.isCountChange)
&& Objects.equals(delegated, that.delegated)
&& Objects.equals(created, that.created)
&& Objects.equals(updated, that.updated)
&& Objects.equals(destroyed, that.destroyed);
}
return false;
}
@Override
public final int hashCode() {
return Objects.hash(accountId, state, date, isCountChange, delegated, created, updated, destroyed);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("accountId", accountId)
.add("state", state)
.add("date", date)
.add("isCountChange", isCountChange)
.add("isDelegated", delegated)
.add("created", created)
.add("updated", updated)
.add("destroyed", destroyed)
.toString();
}
}