You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2018/06/15 14:39:40 UTC
james-project git commit: JAMES-2321 WebAdmin should allow to see
more details about Mails
Repository: james-project
Updated Branches:
refs/heads/master dc5cefcd7 -> c94d35e22
JAMES-2321 WebAdmin should allow to see more details about Mails
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c94d35e2
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c94d35e2
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c94d35e2
Branch: refs/heads/master
Commit: c94d35e22ea841c67054ad2247fe76ff3d20a61a
Parents: dc5cefc
Author: Gautier DI FOLCO <gd...@linagora.com>
Authored: Mon Jun 4 12:20:39 2018 +0200
Committer: Gautier DI FOLCO <gd...@linagora.com>
Committed: Fri Jun 15 15:53:12 2018 +0200
----------------------------------------------------------------------
.../james/core/builder/MimeMessageBuilder.java | 7 +
.../core/builder/MimeMessageBuilderTest.java | 13 +
server/protocols/webadmin/webadmin-core/pom.xml | 4 +
.../james/webadmin/utils/JsonTransformer.java | 2 +
.../webadmin/webadmin-mailrepository/pom.xml | 11 +
.../apache/james/webadmin/dto/HeadersDto.java | 36 +++
.../dto/InaccessibleFieldException.java | 42 ++++
.../org/apache/james/webadmin/dto/MailDto.java | 238 +++++++++++++++++-
.../webadmin/routes/MailRepositoriesRoutes.java | 49 +++-
.../service/MailRepositoryStoreService.java | 7 +-
.../routes/MailRepositoriesRoutesTest.java | 244 ++++++++++++++++++-
11 files changed, 639 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/core/src/main/java/org/apache/james/core/builder/MimeMessageBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/james/core/builder/MimeMessageBuilder.java b/core/src/main/java/org/apache/james/core/builder/MimeMessageBuilder.java
index a45746d..5a33892 100644
--- a/core/src/main/java/org/apache/james/core/builder/MimeMessageBuilder.java
+++ b/core/src/main/java/org/apache/james/core/builder/MimeMessageBuilder.java
@@ -81,6 +81,12 @@ public class MimeMessageBuilder {
public static class MultipartBuilder {
private ImmutableList.Builder<BodyPart> bodyParts = ImmutableList.builder();
+ private Optional<String> subType = Optional.empty();
+
+ public MultipartBuilder subType(String subType) {
+ this.subType = Optional.of(subType);
+ return this;
+ }
public MultipartBuilder addBody(BodyPart bodyPart) {
this.bodyParts.add(bodyPart);
@@ -106,6 +112,7 @@ public class MimeMessageBuilder {
public MimeMultipart build() throws MessagingException {
MimeMultipart multipart = new MimeMultipart();
+ subType.ifPresent(Throwing.consumer(multipart::setSubType));
List<BodyPart> bodyParts = this.bodyParts.build();
for (BodyPart bodyPart : bodyParts) {
multipart.addBodyPart(bodyPart);
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/core/src/test/java/org/apache/james/core/builder/MimeMessageBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/james/core/builder/MimeMessageBuilderTest.java b/core/src/test/java/org/apache/james/core/builder/MimeMessageBuilderTest.java
index 1cfdba8..3b401a5 100644
--- a/core/src/test/java/org/apache/james/core/builder/MimeMessageBuilderTest.java
+++ b/core/src/test/java/org/apache/james/core/builder/MimeMessageBuilderTest.java
@@ -61,4 +61,17 @@ public class MimeMessageBuilderTest {
.containsExactly(value);
}
+ @Test
+ public void buildShouldAllowToSpecifyMultipartSubtype() throws Exception {
+ MimeMessage mimeMessage = MimeMessageBuilder.mimeMessageBuilder()
+ .setContent(MimeMessageBuilder.multipartBuilder()
+ .subType("alternative")
+ .addBody(MimeMessageBuilder.bodyPartBuilder().data("Body 1"))
+ .addBody(MimeMessageBuilder.bodyPartBuilder().data("Body 2")))
+ .build();
+
+ assertThat(mimeMessage.getContentType())
+ .startsWith("multipart/alternative");
+ }
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-core/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/pom.xml b/server/protocols/webadmin/webadmin-core/pom.xml
index e86562d..5318d79 100644
--- a/server/protocols/webadmin/webadmin-core/pom.xml
+++ b/server/protocols/webadmin/webadmin-core/pom.xml
@@ -71,6 +71,10 @@
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-guava</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.github.fge</groupId>
<artifactId>throwing-lambdas</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java
index 907cc3f..e8a22e6 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.collect.ImmutableSet;
@@ -56,6 +57,7 @@ public class JsonTransformer implements ResponseTransformer {
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
+ .registerModule(new GuavaModule())
.registerModules(modules);
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/pom.xml b/server/protocols/webadmin/webadmin-mailrepository/pom.xml
index 20ffd89..25d373f 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/pom.xml
+++ b/server/protocols/webadmin/webadmin-mailrepository/pom.xml
@@ -86,6 +86,12 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>javax-mail-extension</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
@@ -104,6 +110,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>net.javacrumbs.json-unit</groupId>
+ <artifactId>json-unit-fluent</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/HeadersDto.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/HeadersDto.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/HeadersDto.java
new file mode 100644
index 0000000..1194ff9
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/HeadersDto.java
@@ -0,0 +1,36 @@
+/****************************************************************
+ * 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.webadmin.dto;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.collect.ImmutableListMultimap;
+
+public class HeadersDto {
+ private ImmutableListMultimap<String, String> headers;
+
+ public HeadersDto(ImmutableListMultimap<String, String> headers) {
+ this.headers = headers;
+ }
+
+ @JsonValue
+ public ImmutableListMultimap<String, String> getHeaders() {
+ return headers;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/InaccessibleFieldException.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/InaccessibleFieldException.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/InaccessibleFieldException.java
new file mode 100644
index 0000000..d57666b
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/InaccessibleFieldException.java
@@ -0,0 +1,42 @@
+/****************************************************************
+ * 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.webadmin.dto;
+
+import org.apache.james.webadmin.dto.MailDto.AdditionalField;
+
+public class InaccessibleFieldException extends Exception {
+
+ private final AdditionalField field;
+ private final Exception cause;
+
+ public InaccessibleFieldException(AdditionalField field, Exception cause) {
+ super(cause);
+ this.field = field;
+ this.cause = cause;
+ }
+
+ public AdditionalField getField() {
+ return field;
+ }
+
+ public Exception getCause() {
+ return cause;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailDto.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailDto.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailDto.java
index 4587412..defbcbc 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailDto.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailDto.java
@@ -19,24 +19,161 @@
package org.apache.james.webadmin.dto;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import javax.mail.Header;
import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
import org.apache.james.core.MailAddress;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.mime4j.util.MimeUtil;
+import org.apache.james.util.mime.MessageContentExtractor;
+import org.apache.james.util.mime.MessageContentExtractor.MessageContent;
+import org.apache.james.util.streams.Iterators;
import org.apache.mailet.Mail;
+import org.apache.mailet.PerRecipientHeaders;
+import com.github.fge.lambdas.Throwing;
import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
public class MailDto {
- public static MailDto fromMail(Mail mail) throws MessagingException {
+ public static MailDto fromMail(Mail mail, Set<AdditionalField> additionalFields) throws MessagingException, InaccessibleFieldException {
+ Optional<MessageContent> messageContent = fetchMessage(additionalFields, mail);
return new MailDto(mail.getName(),
Optional.ofNullable(mail.getSender()).map(MailAddress::asString),
mail.getRecipients().stream().map(MailAddress::asString).collect(Guavate.toImmutableList()),
Optional.ofNullable(mail.getErrorMessage()),
- Optional.ofNullable(mail.getState()));
+ Optional.ofNullable(mail.getState()),
+ Optional.ofNullable(mail.getRemoteHost()),
+ Optional.ofNullable(mail.getRemoteAddr()),
+ Optional.ofNullable(mail.getLastUpdated()),
+ fetchAttributes(additionalFields, mail),
+ fetchPerRecipientsHeaders(additionalFields, mail),
+ fetchHeaders(additionalFields, mail),
+ fetchTextBody(additionalFields, messageContent),
+ fetchHtmlBody(additionalFields, messageContent),
+ fetchMessageSize(additionalFields, mail));
+ }
+
+ private static Optional<Long> fetchMessageSize(Set<AdditionalField> additionalFields, Mail mail) throws InaccessibleFieldException {
+ if (!additionalFields.contains(AdditionalField.MESSAGE_SIZE)) {
+ return Optional.empty();
+ }
+ try {
+ return Optional.of(mail.getMessageSize());
+ } catch (MessagingException e) {
+ throw new InaccessibleFieldException(AdditionalField.MESSAGE_SIZE, e);
+ }
+ }
+
+ private static Optional<String> fetchTextBody(Set<AdditionalField> additionalFields, Optional<MessageContent> messageContent) throws InaccessibleFieldException {
+ if (!additionalFields.contains(AdditionalField.TEXT_BODY)) {
+ return Optional.empty();
+ }
+
+ return messageContent.flatMap(MessageContent::getTextBody);
+ }
+
+ private static Optional<String> fetchHtmlBody(Set<AdditionalField> additionalFields, Optional<MessageContent> messageContent) throws InaccessibleFieldException {
+ if (!additionalFields.contains(AdditionalField.HTML_BODY)) {
+ return Optional.empty();
+ }
+
+ return messageContent.flatMap(MessageContent::getHtmlBody);
+ }
+
+ private static Optional<MessageContent> fetchMessage(Set<AdditionalField> additionalFields, Mail mail) throws InaccessibleFieldException {
+ if (!additionalFields.contains(AdditionalField.TEXT_BODY) && !additionalFields.contains(AdditionalField.HTML_BODY)) {
+ return Optional.empty();
+ }
+
+ try {
+ MessageContentExtractor extractor = new MessageContentExtractor();
+ return Optional.ofNullable(mail.getMessage())
+ .map(Throwing.function(MailDto::convertMessage).sneakyThrow())
+ .map(Throwing.function(extractor::extract).sneakyThrow());
+ } catch (MessagingException e) {
+ if (additionalFields.contains(AdditionalField.TEXT_BODY)) {
+ throw new InaccessibleFieldException(AdditionalField.TEXT_BODY, e);
+ } else {
+ throw new InaccessibleFieldException(AdditionalField.HTML_BODY, e);
+ }
+ }
+ }
+
+ private static Message convertMessage(MimeMessage message) throws IOException, MessagingException {
+ ByteArrayOutputStream rawMessage = new ByteArrayOutputStream();
+ message.writeTo(rawMessage);
+ return Message.Builder
+ .of()
+ .use(MimeConfig.PERMISSIVE)
+ .parse(new ByteArrayInputStream(rawMessage.toByteArray()))
+ .build();
+ }
+
+ private static Optional<HeadersDto> fetchHeaders(Set<AdditionalField> additionalFields, Mail mail) throws InaccessibleFieldException {
+ if (!additionalFields.contains(AdditionalField.HEADERS)) {
+ return Optional.empty();
+ }
+
+ try {
+ return Optional.ofNullable(mail.getMessage())
+ .map(Throwing.function(MailDto::extractHeaders).sneakyThrow());
+ } catch (MessagingException e) {
+ throw new InaccessibleFieldException(AdditionalField.HEADERS, e);
+ }
+ }
+
+ private static HeadersDto extractHeaders(MimeMessage message) throws MessagingException {
+ return new HeadersDto(Collections
+ .list(message.getAllHeaders())
+ .stream()
+ .collect(Guavate.toImmutableListMultimap(Header::getName, (header) -> MimeUtil.unscrambleHeaderValue(header.getValue()))));
+ }
+
+ private static Optional<ImmutableMap<String, HeadersDto>> fetchPerRecipientsHeaders(Set<AdditionalField> additionalFields, Mail mail) {
+ if (!additionalFields.contains(AdditionalField.PER_RECIPIENTS_HEADERS)) {
+ return Optional.empty();
+ }
+ Multimap<MailAddress, PerRecipientHeaders.Header> headersByRecipient = mail
+ .getPerRecipientSpecificHeaders()
+ .getHeadersByRecipient();
+
+ return Optional.of(headersByRecipient
+ .keySet()
+ .stream()
+ .collect(Guavate.toImmutableMap(MailAddress::asString, (address) -> fetchPerRecipientHeader(headersByRecipient, address))));
+ }
+
+ private static HeadersDto fetchPerRecipientHeader(
+ Multimap<MailAddress, PerRecipientHeaders.Header> headersByRecipient,
+ MailAddress address) {
+ return new HeadersDto(headersByRecipient.get(address)
+ .stream()
+ .collect(Guavate.toImmutableListMultimap(PerRecipientHeaders.Header::getName, PerRecipientHeaders.Header::getValue)));
+ }
+
+ private static Optional<ImmutableMap<String, String>> fetchAttributes(Set<AdditionalField> additionalFields, Mail mail) {
+ if (!additionalFields.contains(AdditionalField.ATTRIBUTES)) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Iterators.toStream(mail.getAttributeNames())
+ .collect(Guavate.toImmutableMap(Function.identity(), attributeName -> mail.getAttribute(attributeName).toString())));
}
private final String name;
@@ -44,14 +181,60 @@ public class MailDto {
private final List<String> recipients;
private final Optional<String> error;
private final Optional<String> state;
+ private final Optional<String> remoteHost;
+ private final Optional<String> remoteAddr;
+ private final Optional<Date> lastUpdated;
+ private final Optional<ImmutableMap<String, String>> attributes;
+ private final Optional<ImmutableMap<String, HeadersDto>> perRecipientsHeaders;
+ private final Optional<HeadersDto> headers;
+ private final Optional<String> textBody;
+ private final Optional<String> htmlBody;
+ private final Optional<Long> messageSize;
+
+ public enum AdditionalField {
+ ATTRIBUTES("attributes"),
+ PER_RECIPIENTS_HEADERS("perRecipientsHeaders"),
+ TEXT_BODY("textBody"),
+ HTML_BODY("htmlBody"),
+ HEADERS("headers"),
+ MESSAGE_SIZE("messageSize");
+
+ public static Optional<AdditionalField> find(String fieldName) {
+ return Arrays.stream(values())
+ .filter(value -> value.fieldName.equalsIgnoreCase(fieldName))
+ .findAny();
+ }
+
+ private final String fieldName;
+
+ AdditionalField(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ public String getName() {
+ return fieldName;
+ }
+ }
public MailDto(String name, Optional<String> sender, List<String> recipients, Optional<String> error,
- Optional<String> state) {
+ Optional<String> state, Optional<String> remoteHost, Optional<String> remoteAddr,
+ Optional<Date> lastUpdated, Optional<ImmutableMap<String, String>> attributes,
+ Optional<ImmutableMap<String, HeadersDto>> perRecipientsHeaders, Optional<HeadersDto> headers,
+ Optional<String> textBody, Optional<String> htmlBody, Optional<Long> messageSize) {
this.name = name;
this.sender = sender;
this.recipients = recipients;
this.error = error;
this.state = state;
+ this.remoteHost = remoteHost;
+ this.remoteAddr = remoteAddr;
+ this.lastUpdated = lastUpdated;
+ this.attributes = attributes;
+ this.perRecipientsHeaders = perRecipientsHeaders;
+ this.headers = headers;
+ this.textBody = textBody;
+ this.htmlBody = htmlBody;
+ this.messageSize = messageSize;
}
public String getName() {
@@ -74,6 +257,42 @@ public class MailDto {
return state;
}
+ public Optional<String> getRemoteHost() {
+ return remoteHost;
+ }
+
+ public Optional<String> getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public Optional<Date> getLastUpdated() {
+ return lastUpdated;
+ }
+
+ public Optional<ImmutableMap<String, String>> getAttributes() {
+ return attributes;
+ }
+
+ public Optional<ImmutableMap<String, HeadersDto>> getPerRecipientsHeaders() {
+ return perRecipientsHeaders;
+ }
+
+ public Optional<HeadersDto> getHeaders() {
+ return headers;
+ }
+
+ public Optional<String> getTextBody() {
+ return textBody;
+ }
+
+ public Optional<String> getHtmlBody() {
+ return htmlBody;
+ }
+
+ public Optional<Long> getMessageSize() {
+ return messageSize;
+ }
+
@Override
public final boolean equals(Object o) {
if (o instanceof MailDto) {
@@ -83,13 +302,22 @@ public class MailDto {
&& Objects.equals(this.sender, mailDto.sender)
&& Objects.equals(this.recipients, mailDto.recipients)
&& Objects.equals(this.error, mailDto.error)
- && Objects.equals(this.state, mailDto.state);
+ && Objects.equals(this.state, mailDto.state)
+ && Objects.equals(this.remoteHost, mailDto.remoteHost)
+ && Objects.equals(this.remoteAddr, mailDto.remoteAddr)
+ && Objects.equals(this.lastUpdated, mailDto.lastUpdated)
+ && Objects.equals(this.attributes, mailDto.attributes)
+ && Objects.equals(this.perRecipientsHeaders, mailDto.perRecipientsHeaders)
+ && Objects.equals(this.headers, mailDto.headers)
+ && Objects.equals(this.textBody, mailDto.textBody)
+ && Objects.equals(this.htmlBody, mailDto.htmlBody)
+ && Objects.equals(this.messageSize, mailDto.messageSize);
}
return false;
}
@Override
public final int hashCode() {
- return Objects.hash(name, sender, recipients, error, state);
+ return Objects.hash(name, sender, recipients, error, state, remoteHost, remoteAddr, lastUpdated, attributes, perRecipientsHeaders, headers, textBody, htmlBody, messageSize);
}
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
index 29ea380..53d7a2a 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Supplier;
import javax.inject.Inject;
@@ -48,7 +49,9 @@ import org.apache.james.util.streams.Offset;
import org.apache.james.webadmin.Constants;
import org.apache.james.webadmin.Routes;
import org.apache.james.webadmin.dto.ExtendedMailRepositoryResponse;
+import org.apache.james.webadmin.dto.InaccessibleFieldException;
import org.apache.james.webadmin.dto.MailDto;
+import org.apache.james.webadmin.dto.MailDto.AdditionalField;
import org.apache.james.webadmin.dto.TaskIdDto;
import org.apache.james.webadmin.service.MailRepositoryStoreService;
import org.apache.james.webadmin.service.ReprocessingAllMailsTask;
@@ -60,6 +63,9 @@ import org.apache.james.webadmin.utils.JsonTransformer;
import org.apache.james.webadmin.utils.ParametersExtractor;
import org.eclipse.jetty.http.HttpStatus;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Splitter;
+
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -215,9 +221,8 @@ public class MailRepositoriesRoutes implements Routes {
})
public void defineGetMail() {
service.get(MAIL_REPOSITORIES + "/:encodedUrl/mails/:mailKey", Constants.JSON_CONTENT_TYPE,
- (request, response) -> getMailAsJson(
- decodedRepositoryUrl(request),
- new MailKey(request.params("mailKey"))),
+ (request, response) ->
+ getMailAsJson(decodedRepositoryUrl(request), new MailKey(request.params("mailKey")), request),
jsonTransformer);
service.get(MAIL_REPOSITORIES + "/:encodedUrl/mails/:mailKey", Constants.RFC822_CONTENT_TYPE,
@@ -250,15 +255,37 @@ public class MailRepositoriesRoutes implements Routes {
}
}
- private MailDto getMailAsJson(MailRepositoryUrl url, MailKey mailKey) {
+ private MailDto getMailAsJson(MailRepositoryUrl url, MailKey mailKey, Request request) {
try {
- return repositoryStoreService.retrieveMail(url, mailKey)
+ return repositoryStoreService.retrieveMail(url, mailKey, extractAdditionalFields(request.queryParamOrDefault("additionalFields", "")))
.orElseThrow(mailNotFoundError(mailKey));
} catch (MailRepositoryStore.MailRepositoryStoreException | MessagingException e) {
throw internalServerError(e);
+ } catch (IllegalArgumentException e) {
+ throw invalidField(e);
+ } catch (InaccessibleFieldException e) {
+ throw inaccessibleField(e);
}
}
+ private HaltException inaccessibleField(InaccessibleFieldException e) {
+ return ErrorResponder.builder()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+ .type(ErrorType.SERVER_ERROR)
+ .cause(e)
+ .message("The field '" + e.getField().getName() + "' requested in additionalFields parameter can't be accessed")
+ .haltError();
+ }
+
+ private HaltException invalidField(IllegalArgumentException e) {
+ return ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorType.INVALID_ARGUMENT)
+ .cause(e)
+ .message("The field '" + e.getMessage() + "' can't be requested in additionalFields parameter")
+ .haltError();
+ }
+
private Supplier<HaltException> mailNotFoundError(MailKey mailKey) {
return () -> ErrorResponder.builder()
.statusCode(HttpStatus.NOT_FOUND_404)
@@ -477,4 +504,16 @@ public class MailRepositoriesRoutes implements Routes {
private MailRepositoryUrl decodedRepositoryUrl(Request request) throws UnsupportedEncodingException {
return MailRepositoryUrl.fromEncoded(request.params("encodedUrl"));
}
+
+ private Set<AdditionalField> extractAdditionalFields(String additionalFieldsParam) throws IllegalArgumentException {
+ return Splitter
+ .on(',')
+ .trimResults()
+ .omitEmptyStrings()
+ .splitToList(additionalFieldsParam)
+ .stream()
+ .map((field) -> AdditionalField.find(field).orElseThrow(() -> new IllegalArgumentException(field)))
+ .collect(Guavate.toImmutableSet());
+ }
+
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
index d8e0e95..6c7ed64 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
@@ -21,6 +21,7 @@ package org.apache.james.webadmin.service;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import javax.inject.Inject;
import javax.mail.MessagingException;
@@ -34,7 +35,9 @@ import org.apache.james.task.Task;
import org.apache.james.util.streams.Iterators;
import org.apache.james.util.streams.Limit;
import org.apache.james.util.streams.Offset;
+import org.apache.james.webadmin.dto.InaccessibleFieldException;
import org.apache.james.webadmin.dto.MailDto;
+import org.apache.james.webadmin.dto.MailDto.AdditionalField;
import org.apache.james.webadmin.dto.MailKeyDTO;
import org.apache.james.webadmin.dto.MailRepositoryResponse;
import org.apache.james.webadmin.utils.ErrorResponder;
@@ -82,11 +85,11 @@ public class MailRepositoryStoreService {
return mailRepository.map(Throwing.function(MailRepository::size).sneakyThrow());
}
- public Optional<MailDto> retrieveMail(MailRepositoryUrl url, MailKey mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+ public Optional<MailDto> retrieveMail(MailRepositoryUrl url, MailKey mailKey, Set<AdditionalField> additionalAttributes) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException, InaccessibleFieldException {
MailRepository mailRepository = getRepository(url);
return Optional.ofNullable(mailRepository.retrieve(mailKey))
- .map(Throwing.function(MailDto::fromMail).sneakyThrow());
+ .map(Throwing.function((Mail mail) -> MailDto.fromMail(mail, additionalAttributes)).sneakyThrow());
}
public Optional<MimeMessage> retrieveMessage(MailRepositoryUrl url, MailKey mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
http://git-wip-us.apache.org/repos/asf/james-project/blob/c94d35e2/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index 2fdcea3..6ea3072 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -22,6 +22,9 @@ package org.apache.james.webadmin.routes;
import static com.jayway.restassured.RestAssured.given;
import static com.jayway.restassured.RestAssured.when;
import static com.jayway.restassured.RestAssured.with;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_EXTRA_FIELDS;
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.contains;
@@ -32,6 +35,7 @@ import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isEmptyOrNullString;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -40,10 +44,20 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.core.builder.MimeMessageBuilder.BodyPartBuilder;
import org.apache.james.mailrepository.api.MailKey;
import org.apache.james.mailrepository.api.MailRepositoryStore;
import org.apache.james.mailrepository.api.MailRepositoryUrl;
@@ -66,6 +80,7 @@ import org.apache.james.webadmin.service.ReprocessingService;
import org.apache.james.webadmin.utils.ErrorResponder;
import org.apache.james.webadmin.utils.JsonTransformer;
import org.apache.mailet.Mail;
+import org.apache.mailet.PerRecipientHeaders.Header;
import org.apache.mailet.base.test.FakeMail;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.After;
@@ -467,19 +482,24 @@ public class MailRepositoriesRoutesTest {
@Test
public void retrievingAMailShouldDisplayItsInformation() throws Exception {
when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
-
String name = NAME_1;
String sender = "sender@domain";
String recipient1 = "recipient1@domain";
String recipient2 = "recipient2@domain";
String state = "state";
String errorMessage = "Error: why this mail is stored";
+ String remoteHost = "smtp.domain";
+ String remoteAddr = "66.66.66.66";
+ Date lastUpdated = new Date(07060504030201L);
mailRepository.store(FakeMail.builder()
.name(name)
.sender(sender)
.recipients(recipient1, recipient2)
.state(state)
.errorMessage(errorMessage)
+ .remoteHost(remoteHost)
+ .remoteAddr(remoteAddr)
+ .lastUpdated(lastUpdated)
.build());
when()
@@ -488,9 +508,229 @@ public class MailRepositoriesRoutesTest {
.statusCode(HttpStatus.OK_200)
.body("name", is(name))
.body("sender", is(sender))
+ .body("recipients", containsInAnyOrder(recipient1, recipient2))
.body("state", is(state))
.body("error", is(errorMessage))
- .body("recipients", containsInAnyOrder(recipient1, recipient2));
+ .body("remoteHost", is(remoteHost))
+ .body("remoteAddr", is(remoteAddr))
+ .body("lastUpdated", is(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(ZonedDateTime.ofInstant(lastUpdated.toInstant(), ZoneId.of("UTC")))));
+ }
+
+ @Test
+ public void retrievingAMailShouldDisplayAllAdditionalFieldsWhenRequested() throws Exception {
+ when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
+ String name = NAME_1;
+
+ BodyPartBuilder textMessage = MimeMessageBuilder.bodyPartBuilder()
+ .addHeader("Content-type", "text/plain")
+ .data("My awesome body!!");
+ BodyPartBuilder htmlMessage = MimeMessageBuilder.bodyPartBuilder()
+ .addHeader("Content-type", "text/html")
+ .data("My awesome <em>body</em>!!");
+ MimeMessage mimeMessage = MimeMessageBuilder.mimeMessageBuilder()
+ .addHeader("headerName3", "value5")
+ .addHeader("headerName3", "value8")
+ .addHeader("headerName4", "value6")
+ .addHeader("headerName4", "value7")
+ .setContent(MimeMessageBuilder.multipartBuilder()
+ .subType("alternative")
+ .addBody(textMessage)
+ .addBody(htmlMessage))
+ .build();
+
+ MailAddress recipientHeaderAddress = new MailAddress("third@party");
+ FakeMail mail = FakeMail.builder()
+ .name(name)
+ .attribute("name1", "value1")
+ .attribute("name2", "value2")
+ .mimeMessage(mimeMessage)
+ .size(42424242)
+ .addHeaderForRecipient(Header.builder()
+ .name("headerName1")
+ .value("value1")
+ .build(), recipientHeaderAddress)
+ .addHeaderForRecipient(Header.builder()
+ .name("headerName1")
+ .value("value2")
+ .build(), recipientHeaderAddress)
+ .addHeaderForRecipient(Header.builder()
+ .name("headerName2")
+ .value("value3")
+ .build(), recipientHeaderAddress)
+ .addHeaderForRecipient(Header.builder()
+ .name("headerName2")
+ .value("value4")
+ .build(), recipientHeaderAddress)
+ .build();
+
+ mailRepository.store(mail);
+
+ String jsonAsString =
+ given()
+ .parameters("additionalFields", "attributes,headers,textBody,htmlBody,messageSize,perRecipientsHeaders")
+ .when()
+ .get(URL_ESCAPED_MY_REPO + "/mails/" + name)
+ .then()
+ .extract()
+ .body()
+ .asString();
+
+ assertThatJson(jsonAsString)
+ .when(IGNORING_ARRAY_ORDER)
+ .when(IGNORING_EXTRA_FIELDS)
+ .isEqualTo("{" +
+ " \"name\": \"name1\"," +
+ " \"sender\": null," +
+ " \"recipients\": []," +
+ " \"error\": null," +
+ " \"state\": null," +
+ " \"remoteHost\": \"111.222.333.444\"," +
+ " \"remoteAddr\": \"127.0.0.1\"," +
+ " \"lastUpdated\": null," +
+ " \"attributes\": {" +
+ " \"name2\": \"value2\"," +
+ " \"name1\": \"value1\"" +
+ " }," +
+ " \"perRecipientsHeaders\": {" +
+ " \"third@party\": {" +
+ " \"headerName1\": [" +
+ " \"value1\"," +
+ " \"value2\"" +
+ " ]," +
+ " \"headerName2\": [" +
+ " \"value3\"," +
+ " \"value4\"" +
+ " ]" +
+ " }" +
+ " }," +
+ " \"headers\": {" +
+ " \"headerName4\": [" +
+ " \"value6\"," +
+ " \"value7\"" +
+ " ]," +
+ " \"headerName3\": [" +
+ " \"value5\"," +
+ " \"value8\"" +
+ " ]" +
+ " }," +
+ " \"textBody\": \"My awesome body!!\"," +
+ " \"htmlBody\": \"My awesome <em>body</em>!!\"," +
+ " \"messageSize\": 42424242" +
+ "}");
+ }
+
+ @Test
+ public void retrievingAMailShouldDisplayAllValidAdditionalFieldsWhenRequested() throws Exception {
+ when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
+ String name = NAME_1;
+ String sender = "sender@domain";
+ String recipient1 = "recipient1@domain";
+ int messageSize = 42424242;
+ mailRepository.store(FakeMail.builder()
+ .name(name)
+ .sender(sender)
+ .recipients(recipient1)
+ .size(messageSize)
+ .build());
+
+ given()
+ .parameters("additionalFields", ",,,messageSize")
+ .when()
+ .get(URL_ESCAPED_MY_REPO + "/mails/" + name)
+ .then()
+ .statusCode(HttpStatus.OK_200)
+ .body("name", is(name))
+ .body("sender", is(sender))
+ .body("headers", nullValue())
+ .body("textBody", nullValue())
+ .body("htmlBody", nullValue())
+ .body("messageSize", is(messageSize))
+ .body("attributes", nullValue())
+ .body("perRecipientsHeaders", nullValue());
+ }
+
+ @Test
+ public void retrievingAMailShouldDisplayCorrectlyEncodedHeadersInValidAdditionalFieldsWhenRequested() throws Exception {
+ when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
+ String name = NAME_1;
+ String sender = "sender@domain";
+ String recipient1 = "recipient1@domain";
+ MimeMessage mimeMessage = MimeMessageBuilder.mimeMessageBuilder()
+ .addHeader("friend", "=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fr...@linagora.com>")
+ .build();
+
+ mailRepository.store(FakeMail.builder()
+ .name(name)
+ .sender(sender)
+ .recipients(recipient1)
+ .mimeMessage(mimeMessage)
+ .build());
+
+ given()
+ .parameters("additionalFields", "headers")
+ .when()
+ .get(URL_ESCAPED_MY_REPO + "/mails/" + name)
+ .then()
+ .statusCode(HttpStatus.OK_200)
+ .body("name", is(name))
+ .body("sender", is(sender))
+ .body("headers.friend", is(Arrays.asList("Frédéric MARTIN <fr...@linagora.com>")));
+ }
+
+ @Test
+ public void retrievingAMailShouldDisplayAllValidAdditionalFieldsEvenTheDuplicatedOnesWhenRequested() throws Exception {
+ when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
+ String name = NAME_1;
+ String sender = "sender@domain";
+ String recipient1 = "recipient1@domain";
+ int messageSize = 42424242;
+ mailRepository.store(FakeMail.builder()
+ .name(name)
+ .sender(sender)
+ .recipients(recipient1)
+ .size(messageSize)
+ .build());
+
+ given()
+ .parameters("additionalFields", "messageSize,messageSize")
+ .when()
+ .get(URL_ESCAPED_MY_REPO + "/mails/" + name)
+ .then()
+ .statusCode(HttpStatus.OK_200)
+ .body("name", is(name))
+ .body("sender", is(sender))
+ .body("headers", nullValue())
+ .body("textBody", nullValue())
+ .body("htmlBody", nullValue())
+ .body("messageSize", is(messageSize))
+ .body("attributes", nullValue())
+ .body("perRecipientsHeaders", nullValue());
+ }
+
+ @Test
+ public void retrievingAMailShouldFailWhenAnUnknownFieldIsRequested() throws Exception {
+ when(mailRepositoryStore.get(URL_MY_REPO)).thenReturn(Optional.of(mailRepository));
+ String name = NAME_1;
+ String sender = "sender@domain";
+ String recipient1 = "recipient1@domain";
+ int messageSize = 42424242;
+ mailRepository.store(FakeMail.builder()
+ .name(name)
+ .sender(sender)
+ .recipients(recipient1)
+ .size(messageSize)
+ .build());
+
+ given()
+ .parameters("additionalFields", "nonExistingField")
+ .when()
+ .get(URL_ESCAPED_MY_REPO + "/mails/" + name)
+ .then()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .body("statusCode", is(400))
+ .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+ .body("message", is("The field 'nonExistingField' can't be requested in additionalFields parameter"));
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org