Merge pull request #546 from chibenwa/JAMES-3544-list-buckets
JAMES-3544 Blob store should allow to list bucket
diff --git a/.scalafix.conf b/.scalafix.conf
new file mode 100644
index 0000000..b00dda3
--- /dev/null
+++ b/.scalafix.conf
@@ -0,0 +1,26 @@
+rules = [
+ DisableSyntax,
+ ProcedureSyntax,
+ NoValInForComprehension,
+ LeakingImplicitClassVal
+]
+
+RemoveUnused.imports = true
+RemoveUnused.privates = false
+RemoveUnused.locals = true
+RemoveUnused.patternvars = false
+RemoveUnused.implicits = false
+
+DisableSyntax.noVars = false
+DisableSyntax.noThrows = false
+DisableSyntax.noNulls = false
+DisableSyntax.noReturns = false
+DisableSyntax.noWhileLoops = false
+DisableSyntax.noAsInstanceOf = false
+DisableSyntax.noIsInstanceOf = false
+DisableSyntax.noXml = false
+DisableSyntax.noDefaultArgs = false
+DisableSyntax.noFinalVal = true
+DisableSyntax.noFinalize = true
+DisableSyntax.noValPatterns = false
+DisableSyntax.noUniversalEquality = false
\ No newline at end of file
diff --git a/docs/modules/community/pages/release.adoc b/docs/modules/community/pages/release.adoc
index c66ba2f..f03df3b 100644
--- a/docs/modules/community/pages/release.adoc
+++ b/docs/modules/community/pages/release.adoc
@@ -186,7 +186,7 @@
# $2: Key footprint to use for signing
sha512sum $1 > $1.sha512
-gpg -u $2 --output $1.asc --detach-sig $1
+gpg -u $2 --output $1.asc --detach-sig --armor $1
svn add $1
svn propset svn:mime-type application/octet-stream $1
svn add $1.sha512
diff --git a/event-sourcing/event-sourcing-core/pom.xml b/event-sourcing/event-sourcing-core/pom.xml
index f843e5a..a7b6287 100644
--- a/event-sourcing/event-sourcing-core/pom.xml
+++ b/event-sourcing/event-sourcing-core/pom.xml
@@ -87,6 +87,13 @@
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/event-sourcing/event-sourcing-pojo/pom.xml b/event-sourcing/event-sourcing-pojo/pom.xml
index a6c5cc6..1d24afb 100644
--- a/event-sourcing/event-sourcing-pojo/pom.xml
+++ b/event-sourcing/event-sourcing-pojo/pom.xml
@@ -52,6 +52,13 @@
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
index 6ee804b..b117baf 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
@@ -34,6 +34,7 @@
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.ThreadId;
import org.apache.james.mailbox.model.search.MailboxQuery;
import org.reactivestreams.Publisher;
@@ -322,7 +323,18 @@
* @param session
* the context for this call, not null
*/
- Publisher<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException;
+ Publisher<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit);
+
+ /**
+ * Returns the list of MessageId of messages belonging to that Thread
+ *
+ * @param threadId
+ * target Thread
+ * @param session
+ * the context for this call, not null
+ * @return the list of MessageId of messages belonging to that Thread
+ */
+ Publisher<MessageId> getThread(ThreadId threadId, MailboxSession session);
/**
* Does the given mailbox exist?
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/exception/ThreadNotFoundException.java b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/ThreadNotFoundException.java
new file mode 100644
index 0000000..f6f4c3e
--- /dev/null
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/ThreadNotFoundException.java
@@ -0,0 +1,28 @@
+/******************************************************************
+ * 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.mailbox.exception;
+
+import org.apache.james.mailbox.model.ThreadId;
+
+public class ThreadNotFoundException extends MailboxException {
+ public ThreadNotFoundException(ThreadId threadId) {
+ super("Thread " + threadId.getBaseMessageId().serialize() + " can not be found");
+ }
+}
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
index ec49426..919b6ad 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
@@ -21,10 +21,26 @@
import java.util.Objects;
+import javax.inject.Inject;
+
import com.google.common.base.MoreObjects;
public class ThreadId {
+ public static class Factory {
+ private final MessageId.Factory messageIdFactory;
+
+ @Inject
+ public Factory(MessageId.Factory messageIdFactory) {
+ this.messageIdFactory = messageIdFactory;
+ }
+
+ public ThreadId fromString(String serialized) {
+ MessageId messageId = messageIdFactory.fromString(serialized);
+ return fromBaseMessageId(messageId);
+ }
+ }
+
public static ThreadId fromBaseMessageId(MessageId baseMessageId) {
return new ThreadId(baseMessageId);
}
diff --git a/mailbox/event/json/pom.xml b/mailbox/event/json/pom.xml
index e232ab8..055f8ab 100644
--- a/mailbox/event/json/pom.xml
+++ b/mailbox/event/json/pom.xml
@@ -99,6 +99,13 @@
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
diff --git a/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SearchThreadIdGuessingAlgorithmTest.java b/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SearchThreadIdGuessingAlgorithmTest.java
index 83ab8c1..dfa79f2 100644
--- a/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SearchThreadIdGuessingAlgorithmTest.java
+++ b/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SearchThreadIdGuessingAlgorithmTest.java
@@ -19,21 +19,31 @@
package org.apache.james.mailbox.store.search;
+import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.inmemory.InMemoryCombinationManagerTestSystem;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import org.apache.james.mailbox.inmemory.mail.InMemoryMapperProvider;
import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.store.CombinationManagerTestSystem;
import org.apache.james.mailbox.store.ThreadIdGuessingAlgorithmContract;
+import org.apache.james.mailbox.store.mail.MessageMapper;
import org.apache.james.mailbox.store.mail.SearchThreadIdGuessingAlgorithm;
import org.apache.james.mailbox.store.mail.ThreadIdGuessingAlgorithm;
+import org.apache.james.mailbox.store.mail.model.MapperProvider;
public class SearchThreadIdGuessingAlgorithmTest extends ThreadIdGuessingAlgorithmContract {
+ private InMemoryMailboxManager mailboxManager;
@Override
protected CombinationManagerTestSystem createTestingData() {
InMemoryIntegrationResources resources = InMemoryIntegrationResources.defaultResources();
+ mailboxManager = resources.getMailboxManager();
+ eventBus = resources.getEventBus();
+ messageIdFactory = resources.getMessageIdFactory();
+
return new InMemoryCombinationManagerTestSystem(
resources.getMailboxManager(),
resources.getMessageIdManager());
@@ -45,7 +55,22 @@
}
@Override
+ protected MessageMapper createMessageMapper(MailboxSession mailboxSession) {
+ return mailboxManager.getMapperFactory().createMessageMapper(mailboxSession);
+ }
+
+ @Override
+ protected MapperProvider provideMapper() {
+ return new InMemoryMapperProvider();
+ }
+
+ @Override
protected MessageId initNewBasedMessageId() {
return InMemoryMessageId.of(100);
}
+
+ @Override
+ protected MessageId initOtherBasedMessageId() {
+ return InMemoryMessageId.of(1000);
+ }
}
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 e228e7e..85d47e5 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
@@ -74,6 +74,7 @@
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.model.ThreadId;
import org.apache.james.mailbox.model.UidValidity;
import org.apache.james.mailbox.model.search.MailboxQuery;
import org.apache.james.mailbox.model.search.PrefixedWildcard;
@@ -782,14 +783,19 @@
}
@Override
- public Flux<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException {
+ public Flux<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) {
return getInMailboxIds(expression, session)
.filter(id -> !expression.getNotInMailboxes().contains(id))
.collect(Guavate.toImmutableSet())
.flatMapMany(Throwing.function(ids -> index.search(session, ids, expression.getSearchQuery(), limit)));
}
- private Flux<MailboxId> getInMailboxIds(MultimailboxesSearchQuery expression, MailboxSession session) throws MailboxException {
+ @Override
+ public Flux<MessageId> getThread(ThreadId threadId, MailboxSession session) {
+ return threadIdGuessingAlgorithm.getMessageIdsInThread(threadId, session);
+ }
+
+ private Flux<MailboxId> getInMailboxIds(MultimailboxesSearchQuery expression, MailboxSession session) {
if (expression.getInMailboxes().isEmpty()) {
return accessibleMailboxIds(expression.getNamespace(), Right.Read, session);
} else {
@@ -799,7 +805,7 @@
}
}
- private Flux<Mailbox> filterReadable(ImmutableSet<MailboxId> inMailboxes, MailboxSession session) throws MailboxException {
+ private Flux<Mailbox> filterReadable(ImmutableSet<MailboxId> inMailboxes, MailboxSession session) {
MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session);
return Flux.fromIterable(inMailboxes)
.concatMap(mailboxMapper::findMailboxById)
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/NaiveThreadIdGuessingAlgorithm.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/NaiveThreadIdGuessingAlgorithm.java
index b6334ca..eed7eb2 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/NaiveThreadIdGuessingAlgorithm.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/NaiveThreadIdGuessingAlgorithm.java
@@ -28,6 +28,7 @@
import org.apache.james.mailbox.store.mail.model.MimeMessageId;
import org.apache.james.mailbox.store.mail.model.Subject;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class NaiveThreadIdGuessingAlgorithm implements ThreadIdGuessingAlgorithm {
@@ -35,4 +36,9 @@
public Mono<ThreadId> guessThreadIdReactive(MessageId messageId, Optional<MimeMessageId> thisMimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject, MailboxSession session) {
return Mono.just(ThreadId.fromBaseMessageId(messageId));
}
+
+ @Override
+ public Flux<MessageId> getMessageIdsInThread(ThreadId threadId, MailboxSession session) {
+ return Flux.just(threadId.getBaseMessageId());
+ }
}
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
index a229fee..551e737 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
@@ -29,7 +29,7 @@
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageIdManager;
-import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.ThreadNotFoundException;
import org.apache.james.mailbox.model.FetchGroup;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageResult;
@@ -56,7 +56,7 @@
}
@Override
- public Mono<ThreadId> guessThreadIdReactive(MessageId messageId, Optional<MimeMessageId> mimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject, MailboxSession session) throws MailboxException {
+ public Mono<ThreadId> guessThreadIdReactive(MessageId messageId, Optional<MimeMessageId> mimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject, MailboxSession session) {
MultimailboxesSearchQuery expression = buildSearchQuery(mimeMessageId, inReplyTo, references, subject);
return Flux.from(mailboxManager.search(expression, session, 1))
@@ -67,6 +67,21 @@
.switchIfEmpty(Mono.just(ThreadId.fromBaseMessageId(messageId)));
}
+ @Override
+ public Flux<MessageId> getMessageIdsInThread(ThreadId threadId, MailboxSession session) {
+ SearchQuery searchQuery = SearchQuery.builder()
+ .andCriteria(SearchQuery.threadId(threadId))
+ .sorts(new SearchQuery.Sort(SearchQuery.Sort.SortClause.Arrival, SearchQuery.Sort.Order.NATURAL))
+ .build();
+
+ MultimailboxesSearchQuery expression = MultimailboxesSearchQuery
+ .from(searchQuery)
+ .build();
+
+ return Flux.from(mailboxManager.search(expression, session, Integer.MAX_VALUE))
+ .switchIfEmpty(Mono.error(() -> new ThreadNotFoundException(threadId)));
+ }
+
private MultimailboxesSearchQuery buildSearchQuery(Optional<MimeMessageId> mimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject) {
Set<MimeMessageId> mimeMessageIds = buildMimeMessageIdSet(mimeMessageId, inReplyTo, references);
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/ThreadIdGuessingAlgorithm.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/ThreadIdGuessingAlgorithm.java
index 4461fc9..2b6d91a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/ThreadIdGuessingAlgorithm.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/ThreadIdGuessingAlgorithm.java
@@ -23,14 +23,16 @@
import java.util.Optional;
import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.ThreadId;
import org.apache.james.mailbox.store.mail.model.MimeMessageId;
import org.apache.james.mailbox.store.mail.model.Subject;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ThreadIdGuessingAlgorithm {
- Mono<ThreadId> guessThreadIdReactive(MessageId messageId, Optional<MimeMessageId> thisMimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject, MailboxSession session) throws MailboxException;
+ Mono<ThreadId> guessThreadIdReactive(MessageId messageId, Optional<MimeMessageId> thisMimeMessageId, Optional<MimeMessageId> inReplyTo, Optional<List<MimeMessageId>> references, Optional<Subject> subject, MailboxSession session);
+
+ Flux<MessageId> getMessageIdsInThread(ThreadId threadId, MailboxSession session);
}
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/ThreadIdGuessingAlgorithmContract.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/ThreadIdGuessingAlgorithmContract.java
index c28757c..c54ee33 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/ThreadIdGuessingAlgorithmContract.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/ThreadIdGuessingAlgorithmContract.java
@@ -20,22 +20,40 @@
package org.apache.james.mailbox.store;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.nio.charset.StandardCharsets;
+import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
+import javax.mail.Flags;
+
import org.apache.james.core.Username;
+import org.apache.james.events.EventBus;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.events.MailboxIdRegistrationKey;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.ThreadNotFoundException;
+import org.apache.james.mailbox.model.ByteContent;
+import org.apache.james.mailbox.model.Mailbox;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.ThreadId;
+import org.apache.james.mailbox.model.UidValidity;
+import org.apache.james.mailbox.store.event.EventFactory;
+import org.apache.james.mailbox.store.mail.MessageMapper;
import org.apache.james.mailbox.store.mail.ThreadIdGuessingAlgorithm;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.MapperProvider;
import org.apache.james.mailbox.store.mail.model.MimeMessageId;
import org.apache.james.mailbox.store.mail.model.Subject;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.stream.RawField;
import org.junit.jupiter.api.BeforeEach;
@@ -44,35 +62,55 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Flux;
+
public abstract class ThreadIdGuessingAlgorithmContract {
public static final Username USER = Username.of("quan");
+ private static final UidValidity UID_VALIDITY = UidValidity.of(42);
+
+ protected EventBus eventBus;
+ protected MessageId.Factory messageIdFactory;
private MailboxManager mailboxManager;
private MessageManager inbox;
+ private MessageMapper messageMapper;
private ThreadIdGuessingAlgorithm testee;
private MailboxSession mailboxSession;
private CombinationManagerTestSystem testingData;
private MessageId newBasedMessageId;
+ private MessageId otherBasedMessageId;
+ private Mailbox mailbox;
protected abstract CombinationManagerTestSystem createTestingData();
protected abstract ThreadIdGuessingAlgorithm initThreadIdGuessingAlgorithm(CombinationManagerTestSystem testingData);
+ protected abstract MessageMapper createMessageMapper(MailboxSession mailboxSession);
+
+ protected abstract MapperProvider provideMapper();
+
protected abstract MessageId initNewBasedMessageId();
+ protected abstract MessageId initOtherBasedMessageId();
+
@BeforeEach
void setUp() throws Exception {
testingData = createTestingData();
testee = initThreadIdGuessingAlgorithm(testingData);
newBasedMessageId = initNewBasedMessageId();
+ otherBasedMessageId = initOtherBasedMessageId();
mailboxManager = testingData.getMailboxManager();
mailboxSession = mailboxManager.createSystemSession(USER);
mailboxManager.createMailbox(MailboxPath.inbox(USER), mailboxSession);
+ messageMapper = createMessageMapper(mailboxSession);
inbox = mailboxManager.getMailbox(MailboxPath.inbox(USER), mailboxSession);
+ mailbox = inbox.getMailboxEntity();
}
@Test
- void givenNonMailWhenAddAMailThenGuessingThreadIdShouldBasedOnGeneratedMessageId() throws Exception {
+ void givenNonMailWhenAddAMailThenGuessingThreadIdShouldBasedOnGeneratedMessageId() {
ThreadId threadId = testee.guessThreadIdReactive(newBasedMessageId, Optional.of(new MimeMessageId("abc")), Optional.empty(), Optional.empty(), Optional.of(new Subject("test")), mailboxSession).block();
assertThat(threadId.getBaseMessageId()).isEqualTo(newBasedMessageId);
@@ -202,4 +240,86 @@
assertThat(threadId.getBaseMessageId()).isEqualTo(newBasedMessageId);
}
+ @Test
+ void givenThreeMailsInAThreadThenGetThreadShouldReturnAListWithThreeMessageIdsSortedByArrivalDate() throws MailboxException {
+ MailboxMessage message1 = createMessage(mailbox, ThreadId.fromBaseMessageId(newBasedMessageId));
+ MailboxMessage message2 = createMessage(mailbox, ThreadId.fromBaseMessageId(newBasedMessageId));
+ MailboxMessage message3 = createMessage(mailbox, ThreadId.fromBaseMessageId(newBasedMessageId));
+
+ appendMessageThenDispatchAddedEvent(mailbox, message1);
+ appendMessageThenDispatchAddedEvent(mailbox, message2);
+ appendMessageThenDispatchAddedEvent(mailbox, message3);
+
+ Flux<MessageId> messageIds = testee.getMessageIdsInThread(ThreadId.fromBaseMessageId(newBasedMessageId), mailboxSession);
+
+ assertThat(messageIds.collectList().block())
+ .isEqualTo(ImmutableList.of(message1.getMessageId(), message2.getMessageId(), message3.getMessageId()));
+ }
+
+ @Test
+ void givenNonMailInAThreadThenGetThreadShouldThrowThreadNotFoundException() {
+ Flux<MessageId> messageIds = testee.getMessageIdsInThread(ThreadId.fromBaseMessageId(newBasedMessageId), mailboxSession);
+
+ assertThatThrownBy(() -> testee.getMessageIdsInThread(ThreadId.fromBaseMessageId(newBasedMessageId), mailboxSession).collectList().block())
+ .getCause()
+ .isInstanceOf(ThreadNotFoundException.class);
+ }
+
+ @Test
+ void givenAMailInAThreadThenGetThreadShouldReturnAListWithOnlyOneMessageIdInThatThread() throws MailboxException {
+ MailboxMessage message1 = createMessage(mailbox, ThreadId.fromBaseMessageId(newBasedMessageId));
+
+ appendMessageThenDispatchAddedEvent(mailbox, message1);
+
+ Flux<MessageId> messageIds = testee.getMessageIdsInThread(ThreadId.fromBaseMessageId(newBasedMessageId), mailboxSession);
+
+ assertThat(messageIds.collectList().block())
+ .containsOnly(message1.getMessageId());
+ }
+
+ @Test
+ void givenTwoDistinctThreadsThenGetThreadShouldNotReturnUnrelatedMails() throws MailboxException {
+ // given message1 and message2 in thread1, message3 in thread2
+ ThreadId threadId1 = ThreadId.fromBaseMessageId(newBasedMessageId);
+ ThreadId threadId2 = ThreadId.fromBaseMessageId(otherBasedMessageId);
+ MailboxMessage message1 = createMessage(mailbox, threadId1);
+ MailboxMessage message2 = createMessage(mailbox, threadId1);
+ MailboxMessage message3 = createMessage(mailbox, threadId2);
+
+ appendMessageThenDispatchAddedEvent(mailbox, message1);
+ appendMessageThenDispatchAddedEvent(mailbox, message2);
+ appendMessageThenDispatchAddedEvent(mailbox, message3);
+
+ // then get thread2 should not return unrelated message1 and message2
+ Flux<MessageId> messageIds = testee.getMessageIdsInThread(ThreadId.fromBaseMessageId(otherBasedMessageId), mailboxSession);
+
+ assertThat(messageIds.collectList().block())
+ .doesNotContain(message1.getMessageId(), message2.getMessageId());
+ }
+
+ private SimpleMailboxMessage createMessage(Mailbox mailbox, ThreadId threadId) {
+ MessageId messageId = messageIdFactory.generate();
+ String content = "Some content";
+ int bodyStart = 16;
+ return new SimpleMailboxMessage(messageId,
+ threadId,
+ new Date(),
+ content.length(),
+ bodyStart,
+ new ByteContent(content.getBytes()),
+ new Flags(),
+ new PropertyBuilder().build(),
+ mailbox.getMailboxId());
+ }
+
+ private void appendMessageThenDispatchAddedEvent(Mailbox mailbox, MailboxMessage mailboxMessage) throws MailboxException {
+ MessageMetaData messageMetaData = messageMapper.add(mailbox, mailboxMessage);
+ eventBus.dispatch(EventFactory.added()
+ .randomEventId()
+ .mailboxSession(mailboxSession)
+ .mailbox(mailbox)
+ .addMetaData(messageMetaData)
+ .build(),
+ new MailboxIdRegistrationKey(mailbox.getMailboxId())).block();
+ }
}
diff --git a/mdn/pom.xml b/mdn/pom.xml
index 735a372..24b9687 100644
--- a/mdn/pom.xml
+++ b/mdn/pom.xml
@@ -90,6 +90,13 @@
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
diff --git a/pom.xml b/pom.xml
index 2009d47..5ed7565 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1358,6 +1358,12 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-data-ldap</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-data-library</artifactId>
<version>${project.version}</version>
</dependency>
@@ -2819,6 +2825,24 @@
<version>2.7.1</version>
</plugin>
<plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <version>0.1.4_0.9.23</version>
+ <configuration>
+ <config>${basedir}/.scalafix.conf</config>
+ <mode>CHECK</mode>
+ </configuration>
+ <executions>
+ <execution>
+ <id>scala-check-style</id>
+ <goals>
+ <goal>scalafix</goal>
+ </goals>
+ <phase>compile</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
<groupId>org.scalatest</groupId>
<artifactId>scalatest-maven-plugin</artifactId>
<version>2.0.0</version>
@@ -2847,7 +2871,15 @@
<arg>-unchecked</arg>
<arg>-deprecation</arg>
<arg>-explaintypes</arg>
+ <arg>-Ywarn-unused</arg>
</args>
+ <compilerPlugins>
+ <compilerPlugin>
+ <groupId>org.scalameta</groupId>
+ <artifactId>semanticdb-scalac_${scala.version}</artifactId>
+ <version>4.4.23</version>
+ </compilerPlugin>
+ </compilerPlugins>
</configuration>
<executions>
<execution>
diff --git a/server/apps/cassandra-app/pom.xml b/server/apps/cassandra-app/pom.xml
index 51d93b5..11dd2a8 100644
--- a/server/apps/cassandra-app/pom.xml
+++ b/server/apps/cassandra-app/pom.xml
@@ -106,7 +106,6 @@
<dependency>
<groupId>${james.groupId}</groupId>
<artifactId>james-server-data-ldap</artifactId>
- <version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
@@ -126,6 +125,12 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-elasticsearch</artifactId>
</dependency>
<dependency>
diff --git a/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJamesServerTest.java b/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJamesServerTest.java
index 0c3a91a..b248f47 100644
--- a/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJamesServerTest.java
+++ b/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJamesServerTest.java
@@ -29,6 +29,7 @@
import org.apache.commons.net.imap.IMAPClient;
import org.apache.james.core.Domain;
+import org.apache.james.data.LdapTestExtension;
import org.apache.james.data.UsersRepositoryModuleChooser;
import org.apache.james.modules.TestJMAPServerModule;
import org.apache.james.modules.protocols.ImapGuiceProbe;
diff --git a/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJmapJamesServerTest.java b/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJmapJamesServerTest.java
index 536fabc..6bcb98e 100644
--- a/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJmapJamesServerTest.java
+++ b/server/apps/cassandra-app/src/test/java/org/apache/james/CassandraLdapJmapJamesServerTest.java
@@ -19,6 +19,7 @@
package org.apache.james;
+import org.apache.james.data.LdapTestExtension;
import org.apache.james.data.UsersRepositoryModuleChooser;
import org.apache.james.jmap.draft.JmapJamesServerContract;
import org.apache.james.modules.TestJMAPServerModule;
diff --git a/server/apps/distributed-app/pom.xml b/server/apps/distributed-app/pom.xml
index acfaaf7..9e90b98 100644
--- a/server/apps/distributed-app/pom.xml
+++ b/server/apps/distributed-app/pom.xml
@@ -141,7 +141,6 @@
<dependency>
<groupId>${james.groupId}</groupId>
<artifactId>james-server-data-ldap</artifactId>
- <version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
@@ -161,6 +160,12 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-distributed</artifactId>
</dependency>
<dependency>
diff --git a/server/apps/distributed-app/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java b/server/apps/distributed-app/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
index 623d15e..40b6f4b 100644
--- a/server/apps/distributed-app/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
+++ b/server/apps/distributed-app/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import org.apache.commons.net.imap.IMAPClient;
+import org.apache.james.data.LdapTestExtension;
import org.apache.james.data.UsersRepositoryModuleChooser;
import org.apache.james.jmap.draft.JmapJamesServerContract;
import org.apache.james.modules.AwsS3BlobStoreExtension;
diff --git a/server/apps/jpa-app/pom.xml b/server/apps/jpa-app/pom.xml
index cc395da..7727845 100644
--- a/server/apps/jpa-app/pom.xml
+++ b/server/apps/jpa-app/pom.xml
@@ -72,6 +72,12 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-data-ldap</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-common</artifactId>
</dependency>
<dependency>
@@ -82,6 +88,16 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-es-resporter</artifactId>
</dependency>
<dependency>
diff --git a/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesConfiguration.java b/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesConfiguration.java
new file mode 100644
index 0000000..de35304
--- /dev/null
+++ b/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesConfiguration.java
@@ -0,0 +1,127 @@
+/****************************************************************
+ * 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;
+
+import java.io.File;
+import java.util.Optional;
+
+import org.apache.james.data.UsersRepositoryModuleChooser;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.filesystem.api.JamesDirectoriesProvider;
+import org.apache.james.server.core.JamesServerResourceLoader;
+import org.apache.james.server.core.MissingArgumentException;
+import org.apache.james.server.core.configuration.Configuration;
+import org.apache.james.server.core.configuration.FileConfigurationProvider;
+import org.apache.james.server.core.filesystem.FileSystemImpl;
+
+public class JPAJamesConfiguration implements Configuration {
+ public static class Builder {
+ private Optional<String> rootDirectory;
+ private Optional<ConfigurationPath> configurationPath;
+ private Optional<UsersRepositoryModuleChooser.Implementation> usersRepositoryImplementation;
+
+ private Builder() {
+ rootDirectory = Optional.empty();
+ configurationPath = Optional.empty();
+ usersRepositoryImplementation = Optional.empty();
+ }
+
+ public Builder workingDirectory(String path) {
+ rootDirectory = Optional.of(path);
+ return this;
+ }
+
+ public Builder workingDirectory(File file) {
+ rootDirectory = Optional.of(file.getAbsolutePath());
+ return this;
+ }
+
+ public Builder useWorkingDirectoryEnvProperty() {
+ rootDirectory = Optional.ofNullable(System.getProperty(WORKING_DIRECTORY));
+ if (!rootDirectory.isPresent()) {
+ throw new MissingArgumentException("Server needs a working.directory env entry");
+ }
+ return this;
+ }
+
+ public Builder configurationPath(ConfigurationPath path) {
+ configurationPath = Optional.of(path);
+ return this;
+ }
+
+ public Builder configurationFromClasspath() {
+ configurationPath = Optional.of(new ConfigurationPath(FileSystem.CLASSPATH_PROTOCOL));
+ return this;
+ }
+
+ public Builder usersRepository(UsersRepositoryModuleChooser.Implementation implementation) {
+ this.usersRepositoryImplementation = Optional.of(implementation);
+ return this;
+ }
+
+ public JPAJamesConfiguration build() {
+ ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
+ JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
+ .orElseThrow(() -> new MissingArgumentException("Server needs a working.directory env entry")));
+
+ FileSystemImpl fileSystem = new FileSystemImpl(directories);
+
+ FileConfigurationProvider configurationProvider = new FileConfigurationProvider(fileSystem, Basic.builder()
+ .configurationPath(configurationPath)
+ .workingDirectory(directories.getRootDirectory())
+ .build());
+ UsersRepositoryModuleChooser.Implementation usersRepositoryChoice = usersRepositoryImplementation.orElseGet(
+ () -> UsersRepositoryModuleChooser.Implementation.parse(configurationProvider));
+
+ return new JPAJamesConfiguration(
+ configurationPath,
+ directories,
+ usersRepositoryChoice);
+ }
+ }
+
+ public static JPAJamesConfiguration.Builder builder() {
+ return new Builder();
+ }
+
+ private final ConfigurationPath configurationPath;
+ private final JamesDirectoriesProvider directories;
+ private final UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation;
+
+ public JPAJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories, UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation) {
+ this.configurationPath = configurationPath;
+ this.directories = directories;
+ this.usersRepositoryImplementation = usersRepositoryImplementation;
+ }
+
+ @Override
+ public ConfigurationPath configurationPath() {
+ return configurationPath;
+ }
+
+ @Override
+ public JamesDirectoriesProvider directories() {
+ return directories;
+ }
+
+ public UsersRepositoryModuleChooser.Implementation getUsersRepositoryImplementation() {
+ return usersRepositoryImplementation;
+ }
+}
diff --git a/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesServerMain.java b/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesServerMain.java
index 7b33cb6..a840ee6 100644
--- a/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesServerMain.java
+++ b/server/apps/jpa-app/src/main/java/org/apache/james/JPAJamesServerMain.java
@@ -19,9 +19,11 @@
package org.apache.james;
+import org.apache.james.data.UsersRepositoryModuleChooser;
import org.apache.james.modules.MailboxModule;
import org.apache.james.modules.MailetProcessingModule;
import org.apache.james.modules.data.JPADataModule;
+import org.apache.james.modules.data.JPAUsersRepositoryModule;
import org.apache.james.modules.data.SieveJPARepositoryModules;
import org.apache.james.modules.mailbox.DefaultEventModule;
import org.apache.james.modules.mailbox.JPAMailboxModule;
@@ -51,7 +53,6 @@
import org.apache.james.modules.server.WebAdminReIndexingTaskSerializationModule;
import org.apache.james.modules.server.WebAdminServerModule;
import org.apache.james.modules.spamassassin.SpamAssassinListenerModule;
-import org.apache.james.server.core.configuration.Configuration;
import com.google.inject.Module;
import com.google.inject.util.Modules;
@@ -99,7 +100,7 @@
new MailetProcessingModule(), JPA_SERVER_MODULE, PROTOCOLS);
public static void main(String[] args) throws Exception {
- Configuration configuration = Configuration.builder()
+ JPAJamesConfiguration configuration = JPAJamesConfiguration.builder()
.useWorkingDirectoryEnvProperty()
.build();
@@ -110,9 +111,10 @@
JamesServerMain.main(server);
}
- static GuiceJamesServer createServer(Configuration configuration) {
+ static GuiceJamesServer createServer(JPAJamesConfiguration configuration) {
return GuiceJamesServer.forConfiguration(configuration)
- .combineWith(JPA_MODULE_AGGREGATE);
+ .combineWith(JPA_MODULE_AGGREGATE)
+ .combineWith(new UsersRepositoryModuleChooser(new JPAUsersRepositoryModule())
+ .chooseModules(configuration.getUsersRepositoryImplementation()));
}
-
}
diff --git a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerTest.java b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerTest.java
index 9cd0cd6..db1284b 100644
--- a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerTest.java
+++ b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerTest.java
@@ -19,6 +19,7 @@
package org.apache.james;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Durations.FIVE_HUNDRED_MILLISECONDS;
import static org.awaitility.Durations.ONE_MINUTE;
@@ -41,7 +42,12 @@
class JPAJamesServerTest implements JamesServerContract {
@RegisterExtension
- static JamesServerExtension jamesServerExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+ static JamesServerExtension jamesServerExtension = new JamesServerBuilder<JPAJamesConfiguration>(tmpDir ->
+ JPAJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .usersRepository(DEFAULT)
+ .build())
.server(configuration -> JPAJamesServerMain.createServer(configuration)
.overrideWith(new TestJPAConfigurationModule()))
.lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
diff --git a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithAuthenticatedDatabaseSqlValidationTest.java b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithAuthenticatedDatabaseSqlValidationTest.java
index d8359e4..95f14b4 100644
--- a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithAuthenticatedDatabaseSqlValidationTest.java
+++ b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithAuthenticatedDatabaseSqlValidationTest.java
@@ -19,12 +19,19 @@
package org.apache.james;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
+
import org.junit.jupiter.api.extension.RegisterExtension;
class JPAJamesServerWithAuthenticatedDatabaseSqlValidationTest extends JPAJamesServerWithSqlValidationTest {
@RegisterExtension
- static JamesServerExtension jamesServerExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+ static JamesServerExtension jamesServerExtension = new JamesServerBuilder<JPAJamesConfiguration>(tmpDir ->
+ JPAJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .usersRepository(DEFAULT)
+ .build())
.server(configuration -> JPAJamesServerMain.createServer(configuration)
.overrideWith(new TestJPAConfigurationModuleWithSqlValidation.WithDatabaseAuthentication()))
.lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
diff --git a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithNoDatabaseAuthenticaticationSqlValidationTest.java b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithNoDatabaseAuthenticaticationSqlValidationTest.java
index 89f4dc0..2581b10 100644
--- a/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithNoDatabaseAuthenticaticationSqlValidationTest.java
+++ b/server/apps/jpa-app/src/test/java/org/apache/james/JPAJamesServerWithNoDatabaseAuthenticaticationSqlValidationTest.java
@@ -19,12 +19,18 @@
package org.apache.james;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
+
import org.junit.jupiter.api.extension.RegisterExtension;
class JPAJamesServerWithNoDatabaseAuthenticaticationSqlValidationTest extends JPAJamesServerWithSqlValidationTest {
-
@RegisterExtension
- static JamesServerExtension jamesServerExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+ static JamesServerExtension jamesServerExtension = new JamesServerBuilder<JPAJamesConfiguration>(tmpDir ->
+ JPAJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .usersRepository(DEFAULT)
+ .build())
.server(configuration -> JPAJamesServerMain.createServer(configuration)
.overrideWith(new TestJPAConfigurationModuleWithSqlValidation.NoDatabaseAuthentication()))
.lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
diff --git a/server/apps/jpa-app/src/test/java/org/apache/james/JPAWithLDAPJamesServerTest.java b/server/apps/jpa-app/src/test/java/org/apache/james/JPAWithLDAPJamesServerTest.java
new file mode 100644
index 0000000..8d86124
--- /dev/null
+++ b/server/apps/jpa-app/src/test/java/org/apache/james/JPAWithLDAPJamesServerTest.java
@@ -0,0 +1,57 @@
+/****************************************************************
+ * 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;
+
+import static org.apache.james.MailsShouldBeWellReceived.JAMES_SERVER_HOST;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.LDAP;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+
+import org.apache.commons.net.imap.IMAPClient;
+import org.apache.james.data.LdapTestExtension;
+import org.apache.james.modules.protocols.ImapGuiceProbe;
+import org.apache.james.user.ldap.DockerLdapSingleton;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class JPAWithLDAPJamesServerTest {
+ @RegisterExtension
+ static JamesServerExtension jamesServerExtension = new JamesServerBuilder<JPAJamesConfiguration>(tmpDir ->
+ JPAJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .usersRepository(LDAP)
+ .build())
+ .server(configuration -> JPAJamesServerMain.createServer(configuration)
+ .overrideWith(new TestJPAConfigurationModule()))
+ .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+ .extension(new LdapTestExtension())
+ .build();
+
+
+ @Test
+ void userFromLdapShouldLoginViaImapProtocol(GuiceJamesServer server) throws IOException {
+ IMAPClient imapClient = new IMAPClient();
+ imapClient.connect(JAMES_SERVER_HOST, server.getProbe(ImapGuiceProbe.class).getImapPort());
+
+ assertThat(imapClient.login(DockerLdapSingleton.JAMES_USER.asString(), DockerLdapSingleton.PASSWORD)).isTrue();
+ }
+}
diff --git a/server/apps/jpa-app/src/test/java/org/apache/james/JamesCapabilitiesServerTest.java b/server/apps/jpa-app/src/test/java/org/apache/james/JamesCapabilitiesServerTest.java
index 3150e58..c08f88c 100644
--- a/server/apps/jpa-app/src/test/java/org/apache/james/JamesCapabilitiesServerTest.java
+++ b/server/apps/jpa-app/src/test/java/org/apache/james/JamesCapabilitiesServerTest.java
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.james;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -40,7 +41,12 @@
}
@RegisterExtension
- static JamesServerExtension jamesServerExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+ static JamesServerExtension jamesServerExtension = new JamesServerBuilder<JPAJamesConfiguration>(tmpDir ->
+ JPAJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .usersRepository(DEFAULT)
+ .build())
.server(configuration -> JPAJamesServerMain.createServer(configuration)
.overrideWith(new TestJPAConfigurationModule())
.overrideWith(binder -> binder.bind(MailboxManager.class).toInstance(mailboxManager())))
diff --git a/server/apps/jpa-smtp-app/pom.xml b/server/apps/jpa-smtp-app/pom.xml
index ced7ff6..84c8cee 100644
--- a/server/apps/jpa-smtp-app/pom.xml
+++ b/server/apps/jpa-smtp-app/pom.xml
@@ -69,6 +69,13 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-data-ldap</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-common</artifactId>
</dependency>
<dependency>
@@ -79,6 +86,10 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-es-resporter</artifactId>
</dependency>
<dependency>
diff --git a/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesConfiguration.java b/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesConfiguration.java
new file mode 100644
index 0000000..9614230
--- /dev/null
+++ b/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesConfiguration.java
@@ -0,0 +1,127 @@
+/****************************************************************
+ * 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;
+
+import java.io.File;
+import java.util.Optional;
+
+import org.apache.james.data.UsersRepositoryModuleChooser;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.filesystem.api.JamesDirectoriesProvider;
+import org.apache.james.server.core.JamesServerResourceLoader;
+import org.apache.james.server.core.MissingArgumentException;
+import org.apache.james.server.core.configuration.Configuration;
+import org.apache.james.server.core.configuration.FileConfigurationProvider;
+import org.apache.james.server.core.filesystem.FileSystemImpl;
+
+public class JPAJamesConfiguration implements Configuration {
+ public static class Builder {
+ private Optional<String> rootDirectory;
+ private Optional<ConfigurationPath> configurationPath;
+ private Optional<UsersRepositoryModuleChooser.Implementation> usersRepositoryImplementation;
+
+ private Builder() {
+ rootDirectory = Optional.empty();
+ configurationPath = Optional.empty();
+ usersRepositoryImplementation = Optional.empty();
+ }
+
+ public Builder workingDirectory(String path) {
+ rootDirectory = Optional.of(path);
+ return this;
+ }
+
+ public Builder workingDirectory(File file) {
+ rootDirectory = Optional.of(file.getAbsolutePath());
+ return this;
+ }
+
+ public Builder useWorkingDirectoryEnvProperty() {
+ rootDirectory = Optional.ofNullable(System.getProperty(WORKING_DIRECTORY));
+ if (!rootDirectory.isPresent()) {
+ throw new MissingArgumentException("Server needs a working.directory env entry");
+ }
+ return this;
+ }
+
+ public Builder configurationPath(ConfigurationPath path) {
+ configurationPath = Optional.of(path);
+ return this;
+ }
+
+ public Builder configurationFromClasspath() {
+ configurationPath = Optional.of(new ConfigurationPath(FileSystem.CLASSPATH_PROTOCOL));
+ return this;
+ }
+
+ public Builder usersRepository(UsersRepositoryModuleChooser.Implementation implementation) {
+ this.usersRepositoryImplementation = Optional.of(implementation);
+ return this;
+ }
+
+ public JPAJamesConfiguration build() {
+ ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
+ JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
+ .orElseThrow(() -> new MissingArgumentException("Server needs a working.directory env entry")));
+
+ FileSystemImpl fileSystem = new FileSystemImpl(directories);
+
+ FileConfigurationProvider configurationProvider = new FileConfigurationProvider(fileSystem, Basic.builder()
+ .configurationPath(configurationPath)
+ .workingDirectory(directories.getRootDirectory())
+ .build());
+ UsersRepositoryModuleChooser.Implementation usersRepositoryChoice = usersRepositoryImplementation.orElseGet(
+ () -> UsersRepositoryModuleChooser.Implementation.parse(configurationProvider));
+
+ return new JPAJamesConfiguration(
+ configurationPath,
+ directories,
+ usersRepositoryChoice);
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final ConfigurationPath configurationPath;
+ private final JamesDirectoriesProvider directories;
+ private final UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation;
+
+ public JPAJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories, UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation) {
+ this.configurationPath = configurationPath;
+ this.directories = directories;
+ this.usersRepositoryImplementation = usersRepositoryImplementation;
+ }
+
+ @Override
+ public ConfigurationPath configurationPath() {
+ return configurationPath;
+ }
+
+ @Override
+ public JamesDirectoriesProvider directories() {
+ return directories;
+ }
+
+ public UsersRepositoryModuleChooser.Implementation getUsersRepositoryImplementation() {
+ return usersRepositoryImplementation;
+ }
+}
diff --git a/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesServerMain.java b/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesServerMain.java
index bc6d1fd..a7f684c 100644
--- a/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesServerMain.java
+++ b/server/apps/jpa-smtp-app/src/main/java/org/apache/james/JPAJamesServerMain.java
@@ -19,9 +19,11 @@
package org.apache.james;
+import org.apache.james.data.UsersRepositoryModuleChooser;
import org.apache.james.modules.MailetProcessingModule;
import org.apache.james.modules.data.JPADataModule;
import org.apache.james.modules.data.JPAEntityManagerModule;
+import org.apache.james.modules.data.JPAUsersRepositoryModule;
import org.apache.james.modules.protocols.ProtocolHandlerModule;
import org.apache.james.modules.protocols.SMTPServerModule;
import org.apache.james.modules.queue.activemq.ActiveMQQueueModule;
@@ -35,7 +37,6 @@
import org.apache.james.modules.server.RawPostDequeueDecoratorModule;
import org.apache.james.modules.server.TaskManagerModule;
import org.apache.james.modules.server.WebAdminServerModule;
-import org.apache.james.server.core.configuration.Configuration;
import com.google.inject.Module;
import com.google.inject.util.Modules;
@@ -62,7 +63,7 @@
new ElasticSearchMetricReporterModule());
public static void main(String[] args) throws Exception {
- Configuration configuration = Configuration.builder()
+ JPAJamesConfiguration configuration = JPAJamesConfiguration.builder()
.useWorkingDirectoryEnvProperty()
.build();
@@ -72,9 +73,11 @@
JamesServerMain.main(server);
}
- public static GuiceJamesServer createServer(Configuration configuration) {
+ public static GuiceJamesServer createServer(JPAJamesConfiguration configuration) {
return GuiceJamesServer.forConfiguration(configuration)
- .combineWith(JPA_SERVER_MODULE, PROTOCOLS, new DKIMMailetModule());
+ .combineWith(JPA_SERVER_MODULE, PROTOCOLS, new DKIMMailetModule())
+ .combineWith(new UsersRepositoryModuleChooser(new JPAUsersRepositoryModule())
+ .chooseModules(configuration.getUsersRepositoryImplementation()));
}
}
diff --git a/server/apps/jpa-smtp-app/src/test/java/org/apache/james/JPAJamesServerTest.java b/server/apps/jpa-smtp-app/src/test/java/org/apache/james/JPAJamesServerTest.java
index 542022a..cdbeaae 100644
--- a/server/apps/jpa-smtp-app/src/test/java/org/apache/james/JPAJamesServerTest.java
+++ b/server/apps/jpa-smtp-app/src/test/java/org/apache/james/JPAJamesServerTest.java
@@ -19,6 +19,7 @@
package org.apache.james;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
@@ -34,7 +35,6 @@
import org.apache.james.mailrepository.jpa.JPAUrl;
import org.apache.james.modules.protocols.SmtpGuiceProbe;
import org.apache.james.rrt.jpa.model.JPARecipientRewrite;
-import org.apache.james.server.core.configuration.Configuration;
import org.apache.james.user.jpa.model.JPAUser;
import org.junit.After;
import org.junit.Before;
@@ -56,9 +56,10 @@
}
private org.apache.james.GuiceJamesServer createJamesServer() throws IOException {
- Configuration configuration = Configuration.builder()
+ JPAJamesConfiguration configuration = JPAJamesConfiguration.builder()
.workingDirectory(temporaryFolder.newFolder())
.configurationFromClasspath()
+ .usersRepository(DEFAULT)
.build();
return JPAJamesServerMain.createServer(configuration)
diff --git a/server/apps/jpa-smtp-app/src/test/java/org/apache/james/mariadb/JPAMariaDBJamesServerTest.java b/server/apps/jpa-smtp-app/src/test/java/org/apache/james/mariadb/JPAMariaDBJamesServerTest.java
index be9a34b..50df95b 100644
--- a/server/apps/jpa-smtp-app/src/test/java/org/apache/james/mariadb/JPAMariaDBJamesServerTest.java
+++ b/server/apps/jpa-smtp-app/src/test/java/org/apache/james/mariadb/JPAMariaDBJamesServerTest.java
@@ -19,6 +19,7 @@
package org.apache.james.mariadb;
+import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
@@ -28,9 +29,9 @@
import java.nio.charset.StandardCharsets;
import org.apache.james.GuiceJamesServer;
+import org.apache.james.JPAJamesConfiguration;
import org.apache.james.JPAJamesServerMain;
import org.apache.james.modules.protocols.SmtpGuiceProbe;
-import org.apache.james.server.core.configuration.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -62,8 +63,9 @@
}
private org.apache.james.GuiceJamesServer createJamesServer(String mariaDBUrl) throws IOException {
- Configuration configuration = Configuration.builder()
+ JPAJamesConfiguration configuration = JPAJamesConfiguration.builder()
.workingDirectory(temporaryFolder.newFolder())
+ .usersRepository(DEFAULT)
.configurationFromClasspath()
.build();
diff --git a/server/container/guice/data-ldap/pom.xml b/server/container/guice/data-ldap/pom.xml
index 5bc6f60..752804c 100644
--- a/server/container/guice/data-ldap/pom.xml
+++ b/server/container/guice/data-ldap/pom.xml
@@ -43,10 +43,23 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-data-ldap</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-common</artifactId>
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-common</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-testing</artifactId>
<scope>test</scope>
</dependency>
diff --git a/server/apps/cassandra-app/src/test/java/org/apache/james/DockerLdapRule.java b/server/container/guice/data-ldap/src/test/java/org/apache/james/data/DockerLdapRule.java
similarity index 96%
rename from server/apps/cassandra-app/src/test/java/org/apache/james/DockerLdapRule.java
rename to server/container/guice/data-ldap/src/test/java/org/apache/james/data/DockerLdapRule.java
index 4ce36af..2cdada9 100644
--- a/server/apps/cassandra-app/src/test/java/org/apache/james/DockerLdapRule.java
+++ b/server/container/guice/data-ldap/src/test/java/org/apache/james/data/DockerLdapRule.java
@@ -17,9 +17,10 @@
* under the License. *
****************************************************************/
-package org.apache.james;
+package org.apache.james.data;
import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.GuiceModuleTestRule;
import org.apache.james.user.ldap.DockerLdapSingleton;
import org.apache.james.user.ldap.LdapRepositoryConfiguration;
import org.junit.runner.Description;
diff --git a/server/apps/cassandra-app/src/test/java/org/apache/james/LdapTestExtension.java b/server/container/guice/data-ldap/src/test/java/org/apache/james/data/LdapTestExtension.java
similarity index 91%
rename from server/apps/cassandra-app/src/test/java/org/apache/james/LdapTestExtension.java
rename to server/container/guice/data-ldap/src/test/java/org/apache/james/data/LdapTestExtension.java
index a468116..d64312f 100644
--- a/server/apps/cassandra-app/src/test/java/org/apache/james/LdapTestExtension.java
+++ b/server/container/guice/data-ldap/src/test/java/org/apache/james/data/LdapTestExtension.java
@@ -17,8 +17,9 @@
* under the License. *
****************************************************************/
-package org.apache.james;
+package org.apache.james.data;
+import org.apache.james.GuiceModuleTestExtension;
import org.junit.jupiter.api.extension.ExtensionContext;
import com.google.inject.Module;
@@ -27,11 +28,11 @@
private DockerLdapRule ldapRule;
- LdapTestExtension() {
+ public LdapTestExtension() {
this(new DockerLdapRule());
}
- LdapTestExtension(DockerLdapRule ldapRule) {
+ public LdapTestExtension(DockerLdapRule ldapRule) {
this.ldapRule = ldapRule;
}
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
index ee79def..4082070 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
+++ b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
@@ -21,10 +21,8 @@
import com.google.inject.AbstractModule;
public class JPADataModule extends AbstractModule {
-
@Override
protected void configure() {
- install(new JPAUsersRepositoryModule());
install(new JPADomainListModule());
install(new JPARecipientRewriteTableModule());
install(new JPAMailRepositoryModule());
diff --git a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
index 10755c1..56e31fd 100644
--- a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
+++ b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
@@ -173,6 +173,13 @@
return messageManager.appendMessage(appendCommand, mailboxSession).getId();
}
+ public MessageManager.AppendResult appendMessageAndGetAppendResult(String username, MailboxPath mailboxPath, MessageManager.AppendCommand appendCommand)
+ throws MailboxException {
+ MailboxSession mailboxSession = mailboxManager.createSystemSession(Username.of(username));
+ MessageManager messageManager = mailboxManager.getMailbox(mailboxPath, mailboxSession);
+ return messageManager.appendMessage(appendCommand, mailboxSession);
+ }
+
@Override
public Collection<String> listSubscriptions(String user) throws Exception {
MailboxSession mailboxSession = mailboxManager.createSystemSession(Username.of(user));
diff --git a/server/container/guice/pom.xml b/server/container/guice/pom.xml
index ea45f6f..dea05b8 100644
--- a/server/container/guice/pom.xml
+++ b/server/container/guice/pom.xml
@@ -104,6 +104,12 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-guice-data-ldap</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>james-server-guice-distributed</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
index 31cd912..a8a6baf 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
@@ -27,7 +27,6 @@
import org.apache.james.core.MailAddress;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
import org.apache.james.mailbox.model.SearchQuery;
@@ -106,7 +105,7 @@
.from(SearchQuery.of(SearchQuery.mimeMessageID(messageId)))
.build();
return Flux.from(mailboxManager.search(searchByRFC822MessageId, session, limit)).toStream().findFirst();
- } catch (MailboxException | UsersRepositoryException e) {
+ } catch (UsersRepositoryException e) {
LOGGER.error("unable to find message with Message-Id: " + messageId, e);
}
return Optional.empty();
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
index ae1eeb0..3811480 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
@@ -83,6 +83,13 @@
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.parent.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
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/EmailChangesMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailChangesMethodContract.scala
index 59222f7..0ce6bee 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailChangesMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailChangesMethodContract.scala
@@ -210,15 +210,15 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build
val messageId1: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
- val state1: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
+ waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
val messageId2: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
- val state2: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
+ waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
val messageId3: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
- val state3: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
+ waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
val messageId4: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
- val state4: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
+ waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
val messageId5: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
- val state5: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
+ waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
val messageId6: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
val state6: State = waitForNextState(server, AccountId.fromUsername(BOB), State.INITIAL)
@@ -312,7 +312,7 @@
val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
val path: MailboxPath = MailboxPath.forUser(BOB, "mailbox1")
- val mailboxId1 = mailboxProbe.createMailbox(path)
+ mailboxProbe.createMailbox(path)
val mailboxId2 = mailboxProbe.createMailbox(MailboxPath.forUser(BOB, "mailbox2"))
val message: Message = Message.Builder
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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index bcfdf8e..8968153 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -3198,7 +3198,7 @@
@Test
def receivedAtPropertyShouldBeReturned(server: GuiceJamesServer): Unit = {
val path = MailboxPath.inbox(BOB)
- val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
val message: Message = Message.Builder
.of
.setSubject("test")
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/EmailImportContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailImportContract.scala
index e8716c5..18f00c4 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailImportContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailImportContract.scala
@@ -672,7 +672,7 @@
@Test
def importShouldFailWhenBlobNotOwned(server: GuiceJamesServer): Unit = {
val andrePath = MailboxPath.inbox(ANDRE)
- val andreId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
val bobPath = MailboxPath.inbox(BOB)
val bobId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
val receivedAt = ZonedDateTime.now().minusDays(1)
@@ -743,7 +743,7 @@
@Test
def importShouldSucceedWhenBlobDelegated(server: GuiceJamesServer): Unit = {
val andrePath = MailboxPath.inbox(ANDRE)
- val andreId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
val bobPath = MailboxPath.inbox(BOB)
val bobId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
val receivedAt = ZonedDateTime.now().minusDays(1)
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 2b19d63..dd7efb8 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
@@ -147,7 +147,7 @@
def hasAttachmentShouldKeepMessageWithAttachmentWhenTrue(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB),
AppendCommand.from(
buildTestMessage))
@@ -459,8 +459,8 @@
@Test
def emailInSharedMailboxesShouldNotBeDisplayedWhenNoExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe.createMailbox(inbox(ANDRE))
+ mailboxProbe
.appendMessage(ANDRE.asString, inbox(ANDRE),
AppendCommand.from(
Message.Builder
@@ -521,7 +521,7 @@
@Test
def emailInSharedMailboxesShouldBeDisplayedWhenExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
+ mailboxProbe.createMailbox(inbox(ANDRE))
val messageId1: MessageId = mailboxProbe
.appendMessage(ANDRE.asString, inbox(ANDRE),
AppendCommand.from(
@@ -585,7 +585,7 @@
def inMailboxFilterShouldReturnEmptyForSharedMailboxesWhenNoExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(ANDRE.asString, inbox(ANDRE),
AppendCommand.from(
Message.Builder
@@ -714,7 +714,7 @@
def inMailboxOtherThanFilterShouldReturnEmptyForSharedMailboxesWhenNoExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(ANDRE.asString, inbox(ANDRE),
AppendCommand.from(
Message.Builder
@@ -775,8 +775,8 @@
def inMailboxOtherThanFilterShouldAcceptSharedMailboxesWhenExtension(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe
.appendMessage(ANDRE.asString, inbox(ANDRE),
AppendCommand.from(
Message.Builder
@@ -851,7 +851,7 @@
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
val messageId1: MessageId = sendMessageToBobInbox(server, message, requestDate)
- val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, mailboxPath, AppendCommand.from(
ClassLoaderUtils.getSystemResourceAsSharedStream("eml/multipart_simple.eml")))
.getMessageId
@@ -989,7 +989,7 @@
@Test
def headerExistsShouldBeCaseInsentive(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe.createMailbox(inbox(BOB))
val messageId1: MessageId = mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
@@ -1000,7 +1000,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1060,7 +1060,7 @@
@Test
def headerShouldAllowToMatchMailWithSpecificHeaderSet(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe.createMailbox(inbox(BOB))
val messageId1: MessageId = mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
@@ -1071,7 +1071,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1131,7 +1131,7 @@
@Test
def headerShouldAllowToMatchMailWithSpecificValueHeaderSet(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe.createMailbox(inbox(BOB))
val messageId1: MessageId = mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
@@ -1142,7 +1142,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1151,7 +1151,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId3: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1212,7 +1212,7 @@
@Test
def headerContainsShouldBeCaseInsentive(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe.createMailbox(inbox(BOB))
val messageId1: MessageId = mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
@@ -1223,7 +1223,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1232,7 +1232,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId3: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1293,8 +1293,8 @@
@Test
def headerShouldRejectWhenMoreThanTwoItems(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val bobInboxId = mailboxProbe.createMailbox(inbox(BOB))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe.createMailbox(inbox(BOB))
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1304,7 +1304,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1313,7 +1313,7 @@
.setBody("testmail", StandardCharsets.UTF_8)
.build))
.getMessageId
- val messageId3: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, inbox(BOB),
AppendCommand.from(
Message.Builder
@@ -1377,7 +1377,7 @@
buildTestMessage))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
ClassLoaderUtils.getSystemResourceAsSharedStream("eml/multipart_simple.eml")))
.getMessageId
@@ -1435,14 +1435,14 @@
val afterRequestDate = Date.from(ZonedDateTime.now().toInstant)
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
- val messageId1: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB),
AppendCommand.builder()
.withInternalDate(beforeRequestDate)
.build(buildTestMessage))
.getMessageId
- val messageId2: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withInternalDate(beforeRequestDate)
.build(ClassLoaderUtils.getSystemResourceAsSharedStream("eml/multipart_simple.eml")))
@@ -1455,7 +1455,7 @@
.build(buildTestMessage))
.getMessageId
- val messageId4: MessageId = mailboxProbe
+ mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withInternalDate(afterRequestDate)
.build(ClassLoaderUtils.getSystemResourceAsSharedStream("eml/multipart_simple.eml")))
@@ -2902,17 +2902,17 @@
@Test
def minSizeShouldBeInclusive(server: GuiceJamesServer): Unit = {
val message1: Message = simpleMessage("short")
- val size1: Int = computeSize(message1)
+ computeSize(message1)
// One char more than message1
val message2: Message = simpleMessage("short!")
val size2: Int = computeSize(message2)
// One char more than message2
val message3: Message = simpleMessage("short!!")
- val size3: Int = computeSize(message3)
+ computeSize(message3)
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(inbox(BOB))
- val id1 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message1)).getMessageId
+ mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message1)).getMessageId
val id2 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message2)).getMessageId
val id3 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message3)).getMessageId
@@ -2959,19 +2959,19 @@
@Test
def maxSizeShouldBeExclusive(server: GuiceJamesServer): Unit = {
val message1: Message = simpleMessage("looooooooooooooong")
- val size1: Int = computeSize(message1)
+ computeSize(message1)
// One char more than message3
val message2: Message = simpleMessage("looooooooooooooong!")
val size2: Int = computeSize(message2)
// One char more than message4
val message3: Message = simpleMessage("looooooooooooooong!!")
- val size3: Int = computeSize(message3)
+ computeSize(message3)
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
mailboxProbe.createMailbox(inbox(BOB))
val id1 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message1)).getMessageId
- val id2 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message2)).getMessageId
- val id3 = mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message3)).getMessageId
+ mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message2)).getMessageId
+ mailboxProbe.appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(message3)).getMessageId
val request =
s"""{
@@ -3894,7 +3894,7 @@
.withInternalDate(Date.from(ZonedDateTime.now().minusDays(2).toInstant))
.build(message))
.getMessageId
- val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withInternalDate(Date.from(ZonedDateTime.now().minusDays(1).toInstant))
.build(message))
@@ -4471,13 +4471,13 @@
.setFrom("user@domain.tld")
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setFrom("other@domain.tld")
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setFrom("yet@other.tld")
@@ -4489,7 +4489,7 @@
.setFrom("yet@other.tld", "user@domain.tld")
.build))
.getMessageId
- val messageId5 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4549,7 +4549,7 @@
.of
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setFrom("user@domain.tld")
@@ -4567,7 +4567,7 @@
.setFrom("display@other.tld")
.build))
.getMessageId
- val messageId4 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4621,13 +4621,13 @@
.setTo("user@domain.tld")
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setTo("other@domain.tld")
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setTo("yet@other.tld")
@@ -4639,7 +4639,7 @@
.setTo("yet@other.tld", "user@domain.tld")
.build))
.getMessageId
- val messageId5 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4699,7 +4699,7 @@
.of
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setTo("user@domain.tld")
@@ -4717,7 +4717,7 @@
.setTo("display@other.tld")
.build))
.getMessageId
- val messageId4 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4771,13 +4771,13 @@
.setCc(DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setCc(DefaultAddressParser.DEFAULT.parseMailbox("other@domain.tld"))
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setCc(DefaultAddressParser.DEFAULT.parseMailbox("yet@other.tld"))
@@ -4790,7 +4790,7 @@
DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
.build))
.getMessageId
- val messageId5 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4850,7 +4850,7 @@
.of
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setCc(DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
@@ -4868,7 +4868,7 @@
.setCc(DefaultAddressParser.DEFAULT.parseMailbox("display@other.tld"))
.build))
.getMessageId
- val messageId4 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -4922,13 +4922,13 @@
.setBcc(DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setBcc(DefaultAddressParser.DEFAULT.parseMailbox("other@domain.tld"))
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setBcc(DefaultAddressParser.DEFAULT.parseMailbox("yet@other.tld"))
@@ -4941,7 +4941,7 @@
DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
.build))
.getMessageId
- val messageId5 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -5001,7 +5001,7 @@
.of
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setBcc(DefaultAddressParser.DEFAULT.parseMailbox("user@domain.tld"))
@@ -5019,7 +5019,7 @@
.setBcc(DefaultAddressParser.DEFAULT.parseMailbox("display@other.tld"))
.build))
.getMessageId
- val messageId4 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -5072,13 +5072,13 @@
.setSubject("Yet another day in paradise")
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setSubject("Welcome to hell")
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -5131,13 +5131,13 @@
.setSubject("Yet another day in paradise")
.build))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder
.setSubject("Welcome to hell")
.build))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(
messageBuilder.build))
.getMessageId
@@ -5642,11 +5642,11 @@
def emailQueryShouldSupportAndOperator(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("another_custom")).build(message))
.getMessageId
@@ -5714,7 +5714,7 @@
.build(message))
.getMessageId
- val messageId4 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().build(message))
.getMessageId
@@ -5761,19 +5761,19 @@
def emailQueryShouldSupportNotOperator(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withFlags(new Flags("custom"))
.build(message))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withFlags(new Flags("another_custom"))
.build(message))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
.withFlags(new FlagsBuilder().add("custom", "another_custom").build())
.build(message))
@@ -5926,15 +5926,15 @@
def inMailboxShouldBeRejectedWhenInOperator(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("another_custom")).build(message))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new FlagsBuilder().add("custom", "another_custom").build()).build(message))
.getMessageId
@@ -5989,15 +5989,15 @@
def inMailboxOtherThanShouldBeRejectedWhenInOperator(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("another_custom")).build(message))
.getMessageId
- val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new FlagsBuilder().add("custom", "another_custom").build()).build(message))
.getMessageId
@@ -6096,11 +6096,11 @@
def nestedOperatorsShouldBeSupported(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
.getMessageId
- val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("another_custom")).build(message))
.getMessageId
@@ -6197,7 +6197,7 @@
val message: Message = buildTestMessage
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
- val messageId1 = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.appendMessage(BOB.asString, MailboxPath.inbox(BOB),
AppendCommand.builder()
.withFlags(new FlagsBuilder().add("custom_1", "custom_2").build())
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/EmailSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
index 413fdd8..f77c7c1 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
@@ -1101,7 +1101,7 @@
@Test
def createShouldRejectEmptyMailboxIds(server: GuiceJamesServer): Unit = {
val andrePath = MailboxPath.inbox(ANDRE)
- val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
val request =
s"""{
@@ -1142,7 +1142,7 @@
@Test
def createShouldRejectInvalidMailboxIds(server: GuiceJamesServer): Unit = {
val andrePath = MailboxPath.inbox(ANDRE)
- val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
val request =
s"""{
@@ -1185,7 +1185,7 @@
@Test
def createShouldRejectNoMailboxIds(server: GuiceJamesServer): Unit = {
val andrePath = MailboxPath.inbox(ANDRE)
- val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
val request =
s"""{
@@ -2923,7 +2923,7 @@
val messageId = responseAsJson
.\("id")
.get.asInstanceOf[JsString].value
- val size = responseAsJson
+ responseAsJson
.\("size")
.get.asInstanceOf[JsNumber].value
@@ -3255,7 +3255,7 @@
val payload = "123456789\r\n".getBytes(StandardCharsets.UTF_8)
val htmlBody: String = "<!DOCTYPE html><html><head><title></title></head><body><div>I have the most <b>brilliant</b> plan. Let me tell you all about it. What we do is, we</div></body></html>"
- val uploadResponse: String = `given`
+ `given`
.basePath("")
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
.contentType("text/plain")
@@ -5522,7 +5522,7 @@
@Test
def invalidPatchPropertyShouldFail(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val mailboxId1: MailboxId = mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
+ mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
val messageId: MessageId = mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB),
@@ -5571,7 +5571,7 @@
@Test
def invalidMailboxPartialUpdatePropertyShouldFail(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val mailboxId1: MailboxId = mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
+ mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
val messageId: MessageId = mailboxProbe
.appendMessage(BOB.asString, MailboxPath.inbox(BOB),
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/MailboxChangesMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxChangesMethodContract.scala
index 1c1f40a..dad2ea1 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxChangesMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxChangesMethodContract.scala
@@ -1054,7 +1054,7 @@
val provisioningState: State = provisionSystemMailboxes(server)
val path1 = MailboxPath.forUser(BOB, "mailbox1")
- val mailboxId1: String = mailboxProbe
+ mailboxProbe
.createMailbox(path1)
.serialize
@@ -1226,7 +1226,7 @@
.createMailbox(MailboxPath.forUser(BOB, "mailbox5"))
.serialize
- val mailboxId6: String = mailboxProbe
+ mailboxProbe
.createMailbox(MailboxPath.forUser(BOB, "mailbox6"))
.serialize
@@ -1634,7 +1634,7 @@
.setSubject("test")
.setBody("testmail", StandardCharsets.UTF_8)
.build
- val messageId1: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
+ mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
val messageId2: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
val messageId3: MessageId = mailboxProbe.appendMessage(BOB.asString(), path, AppendCommand.from(message)).getMessageId
@@ -1977,7 +1977,7 @@
}
private def provisionSystemMailboxes(server: GuiceJamesServer): State = {
- val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
val jmapGuiceProbe: JmapGuiceProbe = server.getProbe(classOf[JmapGuiceProbe])
val request =
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/MailboxQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
index 8cd1898..25ad0b2 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
@@ -184,7 +184,7 @@
@Test
def queryByRoleShouldNotReturnDelegatedMailboxes(server: GuiceJamesServer): Unit = {
val andreInbox = MailboxPath.inbox(ANDRE)
- val andreInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.createMailbox(andreInbox)
val bobInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
.createMailbox(MailboxPath.inbox(BOB))
@@ -245,7 +245,7 @@
@Test
def queryByRoleShouldNotReturnDelegatedMailboxesWhenCaseVariation(server: GuiceJamesServer): Unit = {
val andreInbox = MailboxPath.inbox(ANDRE)
- val andreInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+ server.getProbe(classOf[MailboxProbeImpl])
.createMailbox(andreInbox)
val bobInboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
.createMailbox(MailboxPath.forUser(BOB, "InBoX"))
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 624c428..ea23e11 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
@@ -7644,7 +7644,7 @@
@Test
def updateShouldHandleNotFoundClientId(server: GuiceJamesServer): Unit = {
val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
- val mailboxId: MailboxId = mailboxProbe.createMailbox(MailboxPath.forUser(BOB, "mailbox"))
+ mailboxProbe.createMailbox(MailboxPath.forUser(BOB, "mailbox"))
val response = `given`
.header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
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/ThreadGetContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/ThreadGetContract.scala
index 04cfdd8..d31acd0 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/ThreadGetContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/ThreadGetContract.scala
@@ -19,6 +19,8 @@
package org.apache.james.jmap.rfc8621.contract
+import java.nio.charset.StandardCharsets
+
import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
import io.restassured.RestAssured.{`given`, requestSpecification}
import io.restassured.http.ContentType.JSON
@@ -28,6 +30,11 @@
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, BOB, BOB_PASSWORD, DOMAIN, authScheme, baseRequestSpecBuilder}
+import org.apache.james.mailbox.MessageManager
+import org.apache.james.mailbox.model.MailboxPath
+import org.apache.james.mime4j.dom.Message
+import org.apache.james.mime4j.stream.RawField
+import org.apache.james.modules.MailboxProbeImpl
import org.apache.james.utils.DataProbeImpl
import org.junit.jupiter.api.{BeforeEach, Test}
@@ -46,7 +53,7 @@
}
@Test
- def threadsShouldReturnSuppliedIds(): Unit = {
+ def givenNonMessageThenGetThreadsShouldReturnNotFound(): Unit = {
val request =
s"""{
| "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
@@ -62,47 +69,6 @@
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]")
- .isEqualTo(
- s"""{
- | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "state": "${SESSION_STATE.value}",
- | "list": [
- | {
- | "id": "123456",
- | "emailIds": ["123456"]
- | }
- | ]
- |}""".stripMargin)
- }
-
- @Test
- def threadsShouldReturnSuppliedIdsWhenSeveralThreads(): Unit = {
- val request =
- s"""{
- | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
- | "methodCalls": [[
- | "Thread/get",
- | {
- | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": ["123456", "789"]
- | },
- | "c1"]]
- |}""".stripMargin
-
- val response = `given`
- .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
- .body(request)
.when
.post
.`then`
@@ -113,22 +79,26 @@
.asString
assertThatJson(response)
- .inPath("methodResponses[0][1]")
.isEqualTo(
s"""{
- | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "state": "${SESSION_STATE.value}",
- | "list": [
- | {
- | "id": "123456",
- | "emailIds": ["123456"]
- | },
- | {
- | "id": "789",
- | "emailIds": ["789"]
- | }
- | ]
- |}""".stripMargin)
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [
+ |
+ | ],
+ | "notFound": [
+ | "123456"
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
}
@Test
@@ -172,4 +142,409 @@
| ]
|}""".stripMargin)
}
+
+ @Test
+ def addRelatedMailsInAThreadThenGetThatThreadShouldReturnExactThreadObjectWithEmailIdsSortedByArrivalDate(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ // given 3 mails with related Subject and related Mime Message-ID fields
+ val message1: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test")
+ .setMessageId("Message-ID-1")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message2 reply to message1
+ val message2: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Re: Test")
+ .setMessageId("Message-ID-2")
+ .setField(new RawField("In-Reply-To", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message3 related to message1 through Subject and References message1's Message-ID
+ val message3: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Fwd: Re: Test")
+ .setMessageId("Message-ID-3")
+ .setField(new RawField("In-Reply-To", "Random-InReplyTo"))
+ .addField(new RawField("References", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ val threadId = message1.getThreadId.serialize()
+ val message1Id = message1.getId.getMessageId.serialize()
+ val message2Id = message2.getId.getMessageId.serialize()
+ val message3Id = message3.getId.getMessageId.serialize()
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["$threadId"]
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ 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)
+ .isEqualTo(
+ s"""{
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [{
+ | "id": "$threadId",
+ | "emailIds": ["$message1Id", "$message2Id", "$message3Id"]
+ | }],
+ | "notFound": [
+ |
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def givenTwoThreadGetThatTwoThreadShouldReturnExactTwoThreadObjectWithEmailIdsSortedByArrivalDate(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ // given 2 mails with related Subject and related Mime Message-ID fields in threadA
+ val message1: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test")
+ .setMessageId("Message-ID-1")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+ // message2 reply to message1
+ val message2: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Re: Test")
+ .setMessageId("Message-ID-2")
+ .setField(new RawField("In-Reply-To", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+ val threadA = message1.getThreadId.serialize()
+
+ // message3 in threadB
+ val message3: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Message3-SubjectLine")
+ .setMessageId("Message-ID-3")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+ val threadB = message3.getThreadId.serialize()
+
+ val message1Id = message1.getId.getMessageId.serialize()
+ val message2Id = message2.getId.getMessageId.serialize()
+ val message3Id = message3.getId.getMessageId.serialize()
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["$threadA", "$threadB"]
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ 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)
+ .isEqualTo(
+ s"""{
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [{
+ | "id": "$threadA",
+ | "emailIds": [
+ | "$message1Id",
+ | "$message2Id"
+ | ]
+ | },
+ | {
+ | "id": "$threadB",
+ | "emailIds": [
+ | "$message3Id"
+ | ]
+ | }
+ | ],
+ | "notFound": [
+ |
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def givenOneThreadGetTwoThreadShouldReturnOnlyOneThreadObjectAndNotFound(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ // given 2 mails with related Subject and related Mime Message-ID fields in threadA
+ val message1: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test")
+ .setMessageId("Message-ID-1")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+ // message2 reply to message1
+ val message2: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Re: Test")
+ .setMessageId("Message-ID-2")
+ .setField(new RawField("In-Reply-To", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+ val threadA = message1.getThreadId.serialize()
+
+ val message1Id = message1.getId.getMessageId.serialize()
+ val message2Id = message2.getId.getMessageId.serialize()
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["$threadA", "nonExistThread"]
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ 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)
+ .isEqualTo(
+ s"""{
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [{
+ | "id": "$threadA",
+ | "emailIds": [
+ | "$message1Id",
+ | "$message2Id"
+ | ]
+ | }],
+ | "notFound": [
+ | "nonExistThread"
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def addThreeMailsWithRelatedSubjectButNonIdenticalMimeMessageIDThenGetThatThreadShouldNotReturnUnrelatedMails(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ val message1: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test")
+ .setMessageId("Message-ID-1")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message2 have related subject with message1 but non identical Mime Message-ID
+ val message2: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Re: Test")
+ .setMessageId("Message-ID-2")
+ .setField(new RawField("In-Reply-To", "Random-InReplyTo"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message3 have related subject with message1 but non identical Mime Message-ID
+ val message3: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Fwd: Re: Test")
+ .setMessageId("Message-ID-3")
+ .setField(new RawField("In-Reply-To", "Another-Random-InReplyTo"))
+ .addField(new RawField("References", "Random-References"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ val threadId1 = message1.getThreadId.serialize()
+ val message1Id = message1.getId.getMessageId.serialize()
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["$threadId1"]
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ 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)
+ .isEqualTo(
+ s"""{
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [{
+ | "id": "$threadId1",
+ | "emailIds": ["$message1Id"]
+ | }],
+ | "notFound": [
+ |
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
+ @Test
+ def addThreeMailsWithIdenticalMimeMessageIDButNonRelatedSubjectThenGetThatThreadShouldNotReturnUnrelatedMails(server: GuiceJamesServer): Unit = {
+ val bobPath = MailboxPath.inbox(BOB)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+ val message1: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test1")
+ .setMessageId("Message-ID-1")
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message2 have identical Mime Message-ID with message1 through In-Reply-To field but have non related subject
+ val message2: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test2")
+ .setMessageId("Message-ID-2")
+ .setField(new RawField("In-Reply-To", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ // message2 have identical Mime Message-ID with message1 through References field but have non related subject
+ val message3: MessageManager.AppendResult = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessageAndGetAppendResult(BOB.asString(), bobPath,
+ MessageManager.AppendCommand.from(Message.Builder.of.setSubject("Test3")
+ .setMessageId("Message-ID-3")
+ .setField(new RawField("In-Reply-To", "Random-InReplyTo"))
+ .addField(new RawField("References", "Message-ID-1"))
+ .setBody("testmail", StandardCharsets.UTF_8)))
+
+ val threadId1 = message1.getThreadId.serialize()
+ val message1Id = message1.getId.getMessageId.serialize()
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": ["$threadId1"]
+ | },
+ | "c1"]]
+ |}""".stripMargin
+
+ 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)
+ .isEqualTo(
+ s"""{
+ | "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses": [
+ | [
+ | "Thread/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "list": [{
+ | "id": "$threadId1",
+ | "emailIds": ["$message1Id"]
+ | }],
+ | "notFound": [
+ |
+ | ]
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+ }
+
}
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/WebSocketContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
index 0223d8a..45ae356 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
@@ -1069,7 +1069,7 @@
def pushEnableRequestWithPushStateShouldReturnServerState(server: GuiceJamesServer): Unit = {
val bobPath = MailboxPath.inbox(BOB)
val accountId: AccountId = AccountId.fromUsername(BOB)
- val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
Thread.sleep(100)
diff --git a/server/protocols/jmap-rfc-8621/pom.xml b/server/protocols/jmap-rfc-8621/pom.xml
index 526270c..42d6fb0 100644
--- a/server/protocols/jmap-rfc-8621/pom.xml
+++ b/server/protocols/jmap-rfc-8621/pom.xml
@@ -189,6 +189,13 @@
<groupId>org.scalatest</groupId>
<artifactId>scalatest-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>io.github.evis</groupId>
+ <artifactId>scalafix-maven-plugin</artifactId>
+ <configuration>
+ <config>${project.parent.parent.basedir}/.scalafix.conf</config>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala
index 140ecfb..37cdd28 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala
@@ -25,7 +25,6 @@
import org.apache.james.core.Username
import org.apache.james.mailbox.MailboxSession
import org.apache.james.metrics.api.MetricFactory
-import org.apache.james.metrics.api.TimeMetric.ExecutionResult.DEFAULT_100_MS_THRESHOLD
import org.apache.james.user.api.{AlreadyExistInUsersRepositoryException, UsersRepository, UsersRepositoryException}
import reactor.core.scala.publisher.SMono
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index 2223077..dbe8136 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -29,7 +29,6 @@
import play.api.libs.functional.syntax._
import play.api.libs.json._
-import scala.language.implicitConversions
object EmailBodyPartToSerialize {
def from(part: EmailBodyPart): EmailBodyPartToSerialize = EmailBodyPartToSerialize(
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
index 11ff719..c41e255 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
@@ -25,7 +25,6 @@
import org.apache.james.mailbox.model.{MailboxId, MessageId}
import play.api.libs.json._
-import scala.language.implicitConversions
import scala.util.Try
class EmailQuerySerializer @Inject()(mailboxIdFactory: MailboxId.Factory) {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ThreadSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ThreadSerializer.scala
index 9f4d74b..ca5da3e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ThreadSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ThreadSerializer.scala
@@ -20,16 +20,21 @@
package org.apache.james.jmap.json
import org.apache.james.jmap.core.UuidState
-import org.apache.james.jmap.mail.{Thread, ThreadChangesRequest, ThreadChangesResponse, ThreadGetRequest, ThreadGetResponse}
-import play.api.libs.json.{JsObject, JsResult, JsValue, Json, OWrites, Reads, Writes}
+import org.apache.james.jmap.mail.{Thread, ThreadChangesRequest, ThreadChangesResponse, ThreadGetRequest, ThreadGetResponse, ThreadNotFound, UnparsedThreadId}
+import org.apache.james.mailbox.model.MessageId
+import play.api.libs.json.{JsObject, JsResult, JsString, JsValue, Json, OWrites, Reads, Writes}
import scala.language.implicitConversions
object ThreadSerializer {
+ private implicit val messageIdWrites: Writes[MessageId] = messageId => JsString(messageId.serialize())
+ private implicit val unparsedThreadIdReads: Reads[UnparsedThreadId] = Json.valueReads[UnparsedThreadId]
private implicit val threadGetReads: Reads[ThreadGetRequest] = Json.reads[ThreadGetRequest]
private implicit val threadChangesReads: Reads[ThreadChangesRequest] = Json.reads[ThreadChangesRequest]
private implicit val threadWrites: OWrites[Thread] = Json.writes[Thread]
private implicit val stateWrites: Writes[UuidState] = Json.valueWrites[UuidState]
+ private implicit val unparsedThreadIdWrites: Writes[UnparsedThreadId] = Json.valueWrites[UnparsedThreadId]
+ private implicit val threadNotFoundWrites: Writes[ThreadNotFound] = Json.valueWrites[ThreadNotFound]
private implicit val threadGetWrites: OWrites[ThreadGetResponse] = Json.writes[ThreadGetResponse]
private implicit val changesResponseWrites: OWrites[ThreadChangesResponse] = Json.writes[ThreadChangesResponse]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Thread.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Thread.scala
index e39c20d..e6619a5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Thread.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Thread.scala
@@ -23,15 +23,23 @@
import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
import org.apache.james.jmap.core.{AccountId, UuidState}
import org.apache.james.jmap.method.WithAccountId
+import org.apache.james.mailbox.model.MessageId
-case class Thread(id: Id, emailIds: List[Id])
+case class Thread(id: Id, emailIds: List[MessageId])
case class ThreadGetRequest(accountId: AccountId,
- ids: List[Id]) extends WithAccountId
+ ids: List[UnparsedThreadId]) extends WithAccountId
case class ThreadGetResponse(accountId: AccountId,
state: UuidState,
- list: List[Thread])
+ list: List[Thread],
+ notFound: ThreadNotFound)
+
+case class ThreadNotFound(value: Set[UnparsedThreadId]) {
+ def merge(other: ThreadNotFound): ThreadNotFound = ThreadNotFound(this.value ++ other.value)
+}
+
+case class UnparsedThreadId(id: Id)
case class ThreadChangesRequest(accountId: AccountId,
sinceState: UuidState,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
index 1c3c53e..7b91276 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
@@ -22,31 +22,59 @@
import eu.timepit.refined.auto._
import javax.inject.Inject
import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
-import org.apache.james.jmap.core.Id.Id
import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.core.{Invocation, UuidState}
+import org.apache.james.jmap.core.{AccountId, Invocation, UuidState}
import org.apache.james.jmap.json.{ResponseSerializer, ThreadSerializer}
-import org.apache.james.jmap.mail.{Thread, ThreadGetRequest, ThreadGetResponse}
+import org.apache.james.jmap.mail.{Thread, ThreadGetRequest, ThreadGetResponse, ThreadNotFound, UnparsedThreadId}
import org.apache.james.jmap.routes.SessionSupplier
-import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.model.{ThreadId => JavaThreadId}
+import org.apache.james.mailbox.{MailboxManager, MailboxSession}
import org.apache.james.metrics.api.MetricFactory
import play.api.libs.json.{JsError, JsSuccess}
-import reactor.core.scala.publisher.SMono
+import reactor.core.scala.publisher.{SFlux, SMono}
+
+import scala.util.Try
+
+object ThreadGetResult {
+ def empty: ThreadGetResult = ThreadGetResult(Set.empty, ThreadNotFound(Set.empty))
+
+ def merge(result1: ThreadGetResult, result2: ThreadGetResult): ThreadGetResult = result1.merge(result2)
+
+ def found(thread: Thread): ThreadGetResult =
+ ThreadGetResult(Set(thread), ThreadNotFound(Set.empty))
+
+ def notFound(unparsedThreadId: UnparsedThreadId): ThreadGetResult =
+ ThreadGetResult(Set.empty, ThreadNotFound(Set(unparsedThreadId)))
+}
+
+case class ThreadGetResult(threads: Set[Thread], notFound: ThreadNotFound) {
+ def merge(other: ThreadGetResult): ThreadGetResult =
+ ThreadGetResult(this.threads ++ other.threads, this.notFound.merge(other.notFound))
+
+ def asResponse(accountId: AccountId): ThreadGetResponse =
+ ThreadGetResponse(
+ accountId = accountId,
+ state = UuidState.INSTANCE,
+ list = threads.toList,
+ notFound = notFound)
+}
class ThreadGetMethod @Inject()(val metricFactory: MetricFactory,
- val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[ThreadGetRequest] {
+ val sessionSupplier: SessionSupplier,
+ val threadIdFactory: JavaThreadId.Factory,
+ val mailboxManager: MailboxManager) extends MethodRequiringAccountId[ThreadGetRequest] {
override val methodName: MethodName = MethodName("Thread/get")
override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL)
override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: ThreadGetRequest): SMono[InvocationWithContext] = {
- val response = ThreadGetResponse(accountId = request.accountId,
- state = UuidState.INSTANCE,
- list = retrieveThreads(request.ids))
- SMono.just(InvocationWithContext(invocation = Invocation(
- methodName = methodName,
- arguments = Arguments(ThreadSerializer.serialize(response)),
- methodCallId = invocation.invocation.methodCallId),
- processingContext = invocation.processingContext))
+ getThreadResponse(request, mailboxSession)
+ .reduce(ThreadGetResult.empty)(ThreadGetResult.merge)
+ .map(threadGetResult => threadGetResult.asResponse(request.accountId))
+ .map(threadGetResponse => Invocation(
+ methodName = methodName,
+ arguments = Arguments(ThreadSerializer.serialize(threadGetResponse)),
+ methodCallId = invocation.invocation.methodCallId))
+ .map(InvocationWithContext(_, invocation.processingContext))
}
override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): Either[IllegalArgumentException, ThreadGetRequest] =
@@ -55,7 +83,18 @@
case errors: JsError => Left(new IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
}
- // Naive implementation
- private def retrieveThreads(ids: List[Id]): List[Thread] =
- ids.map(id => Thread(id = id, emailIds = List(id)))
+ private def getThreadResponse(threadGetRequest: ThreadGetRequest,
+ mailboxSession: MailboxSession): SFlux[ThreadGetResult] = {
+ SFlux.fromIterable(threadGetRequest.ids)
+ .flatMap(unparsedThreadId => {
+ Try(threadIdFactory.fromString(unparsedThreadId.id.toString()))
+ .fold(e => SFlux.just(ThreadGetResult.notFound(unparsedThreadId)),
+ threadId => SFlux.fromPublisher(mailboxManager.getThread(threadId, mailboxSession))
+ .collectSeq()
+ .map(seq => Thread(id = unparsedThreadId.id, emailIds = seq.toList))
+ .map(ThreadGetResult.found)
+ .onErrorResume((_ => SMono.just(ThreadGetResult.notFound(unparsedThreadId)))))
+ })
+ }
+
}
diff --git a/src/adr/0048-cleanup-jmap-uploads.md b/src/adr/0048-cleanup-jmap-uploads.md
new file mode 100644
index 0000000..62ad15d
--- /dev/null
+++ b/src/adr/0048-cleanup-jmap-uploads.md
@@ -0,0 +1,87 @@
+# 48. Cleanup of JMAP uploads
+
+Date: 2021-07-21
+
+## Status
+
+Accepted (lazy consensus).
+
+Not yet implemented.
+
+## Context
+
+JMAP allows users to upload binary content called blobs to be later referenced via method calls. This includes but is not
+limited to `Email/set` for specifying the blobId of attachments and `Email/import`.
+
+The [specification](https://jmap.io/spec-core.html#binary-data) strongly encourages enforcing the cleanup of these uploads:
+
+```
+A blob that is not referenced by a JMAP object (e.g., as a message attachment) MAY be deleted by the server to free up
+resources. Uploads (see below) are initially unreferenced blobs.
+
+[...] An unreferenced blob MUST NOT be deleted for at least 1 hour from the time of upload; if reuploaded, the same
+blobId MAY be returned, but this SHOULD reset the expiry time.
+```
+
+Deleting such uploads in a timely manner is important as:
+
+ - It enables freeing server resources.
+ - failing to do so may compromise privacy: content the user have uploaded and long forgotten might still be accessible
+ in the underlying data-store. Failing to delete uploads in a timely fashion may jeopardize for instance GDPR compliance.
+
+Today, uploads are stored along side email attachments. This means:
+ - We can hardly apply a specific lifecycle that cleans up uploads, as distinguishing attachment from uploads is not
+ trivial.
+ - We currently have a complex right resolution system on attachment, handling both the upload case (were the attachment
+ is linked to a user) and the 'true' attachment case (linked to a message, those who can access the message can access
+ the attachment). This leads to sub-optimal code (slow).
+
+## Decision
+
+We need to create a separate interface `UploadRepository` in `data-jmap` to store uploads for each user. We would provide a memory
+implementation as well as a distributed implementation of it.
+
+The distributed implementation would host metadata of the upload in Cassandra, and the content using the BlobStore API,
+so object storage.
+
+This `UploadRepository` would be used by JMAP RFC-8620 to back uploads (instead of the attachment manager), we will
+provide a `BlobResolver` to enable interactions with the uploaded blob. Similarly, we will use the `UploadRepository` to
+back uploads of JMAP draft.
+
+We will implement cleanup of the distributed `UploadRepository`. This will be done via:
+ - TTLs on the Cassandra metadata.
+ - Organisation of the blobs in time ranged buckets, only the two most recent buckets are kept.
+ - A WebAdmin endpoint would allow to plan a CRON triggering the cleanup.
+
+## Consequences
+
+Upon migrating to the `UploadRepository`, previous uploads will not be carried over. No migration plan is provided as
+the impact is minimal. Upload prior this change will never be cleaned up. This is acceptable as JMAP implementations are
+marked as experimental.
+
+We can clean up attachment storage within the `mailbox-api` and its implementation:
+ - Drop `attachmentOwners` cassandra table
+ - Remove `getOwners` `storeAttachmentForOwner` methods in the Attachment mapper
+ - Rename `storeAttachmentsForMessage*` -> `storeAttachments*` in attachment mapper
+ - Simplify resolution logic for `StoreAttachmentManager` (looking message ownership is then enough)
+ - Fusion of `attachmentMessageId` and `attachmentV2` table, `attachmentMessageId` to be dropped in next release,
+ `attachmentV2` can be altered to add the referencing `messageId`, and a migration task will be provided to populate it.
+ In the meantime a fallback strategy can be supplied: If the messageId cell is null we should default to reading the
+ (old) `attachmentMessageId` table.
+
+## Alternatives
+
+[JMAP blob draft](https://datatracker.ietf.org/doc/draft-ietf-jmap-blob/) had been proposed to have the clients explicitly
+delete its uploads once the blob had been used to create other entities, as this extension introduce a mean to delete
+blobs.
+
+However, relying on clients to enforce effective deletion seems brittle as:
+ - In case of client failures (or malicious client), no mechanisms would ensure effective deletion
+ - The main JMAP specification does not mandate nor encourage clients to clean up their uploads using the blob extension
+ and as such interoperability issues would arise.
+
+## References
+
+ - [JIRA](https://issues.apache.org/jira/browse/JAMES-3544)
+ - [PR of this ADR](https://github.com/apache/james-project/pull/544)
+ - [Thread on server-dev mailing list](https://www.mail-archive.com/server-dev@james.apache.org/msg70591.html)
\ No newline at end of file
diff --git a/src/site/xdoc/download.xml b/src/site/xdoc/download.xml
index cc0244e..e9a1f9d 100644
--- a/src/site/xdoc/download.xml
+++ b/src/site/xdoc/download.xml
@@ -164,7 +164,7 @@
[<a href="https://downloads.apache.org/james/server/3.6.0/james-project-3.6.0-source-release.zip.asc">PGP</a>]</li>
<li>Binary (ZIP Format) for Spring wiring:
- <a href="https://www.apache.org/dyn/closer.lua/james/server/3.6.0/james-server-app-3.6.0-app.zip">apache-james-3.6.0-app.zip</a>
+ <a href="https://www.apache.org/dyn/closer.lua/james/server/3.6.0/james-server-app-3.6.0-app.zip">james-server-app-3.6.0-app.zip</a>
[<a href="https://downloads.apache.org/james/server/3.6.0/james-server-app-3.6.0-app.zip.sha512">SHA-512</a>]
[<a href="https://downloads.apache.org/james/server/3.6.0/james-server-app-3.6.0-app.zip.asc">PGP</a>]
</li>
@@ -580,27 +580,6 @@
</section>
- <section name="Apache HUPA">
-
- <p>Apache HUPA 0.0.2 is the latest stable version:</p>
- <ul>
-
- <li>Source (ZIP Format): <a href="https://www.apache.org/dyn/closer.lua/james/0.0.2/hupa-parent-0.0.2-source-release.zip">hupa-parent-0.0.2-source-release.zip</a>
- [<a href="https://downloads.apache.org/james/hupa/0.0.2/hupa-parent-0.0.2-source-release.zip.sha512">SHA-512</a>]
- [<a href="https://downloads.apache.org/james/hupa/0.0.2/hupa-parent-0.0.2-source-release.zip.asc">PGP</a>]
- </li>
-
- <li>Binary (Java WAR): <a href="https://www.apache.org/dyn/closer.lua/james/0.0.2/hupa-0.0.2.war">hupa-0.0.2.war</a>
- [<a href="https://downloads.apache.org/james/hupa/0.0.2/hupa-0.0.2.war.sha512">SHA-512</a>]
- [<a href="https://downloads.apache.org/james/hupa/0.0.2/hupa-0.0.2.war.asc">PGP</a>]
- </li>
-
- <li>Jars (including source and javadocs) for the modules are distributed through the standard
- <a href='https://maven.apache.org'>Maven</a> repositories on <a href="https://repo1.maven.org/maven2/org/apache/james/hupa">https://repo1.maven.org/maven2/org/apache/james/hupa</a>.</li>
- </ul>
-
- </section>
-
</body>
</document>
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index f32ffb4..97bed20 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -15,8 +15,33 @@
Changes to apply between 3.6.x and 3.7.x will be reported here.
Change list:
-
+ - [Adding the threadId to the ElasticSearch index](#adding-the-threadid-to-the-elasticsearch-index)
- [Rework message denormalization](#rework-message-denormalization)
+ - [Adding threadId column to message metadata tables](#adding-threadid-column-to-message-metadata-tables)
+
+### Adding the threadId to the ElasticSearch index
+
+Date 22/07/2021
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3516
+
+Concerned product: Distributed James
+
+Add threadId to James document mapping to enable thread query search.
+
+We already have this field as part of newly created mappings, but we need to explicitly add this field to existing indices by doing:
+```
+curl -X PUT \
+ http://ip:ESport/mailbox_v1/_mapping \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "properties": {
+ "threadId": {
+ "type": "keyword"
+ }
+ }
+}'
+```
### Rework message denormalization