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 ad...@apache.org on 2016/01/18 12:42:08 UTC

svn commit: r1725240 - in /james/project/trunk/server: ./ protocols/jmap-integration-testing/ protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/ protocols/jmap/ protocols/jmap/src/main/java/org/apache/james/jmap/methods/ pr...

Author: aduprat
Date: Mon Jan 18 11:42:07 2016
New Revision: 1725240

URL: http://svn.apache.org/viewvc?rev=1725240&view=rev
Log:
JAMES-1648 Handle header filtering. Contributed by Ouazana <ra...@linagora.com>

Added:
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertyTest.java
Modified:
    james/project/trunk/server/pom.xml
    james/project/trunk/server/protocols/jmap-integration-testing/pom.xml
    james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
    james/project/trunk/server/protocols/jmap/pom.xml
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponse.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponseWriterImpl.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperty.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java

Modified: james/project/trunk/server/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/pom.xml?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/pom.xml (original)
+++ james/project/trunk/server/pom.xml Mon Jan 18 11:42:07 2016
@@ -1399,6 +1399,11 @@
                 <version>1.6.3</version>
             </dependency>
             <dependency>
+                <groupId>com.jayway.jsonpath</groupId>
+                <artifactId>json-path</artifactId>
+                <version>2.1.0</version>
+            </dependency>
+            <dependency>
                 <groupId>com.jayway.restassured</groupId>
                 <artifactId>rest-assured</artifactId>
                 <version>2.6.0</version>

Modified: james/project/trunk/server/protocols/jmap-integration-testing/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap-integration-testing/pom.xml?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap-integration-testing/pom.xml (original)
+++ james/project/trunk/server/protocols/jmap-integration-testing/pom.xml Mon Jan 18 11:42:07 2016
@@ -183,7 +183,6 @@
                     <groupId>com.jayway.jsonpath</groupId>
                     <artifactId>json-path</artifactId>
                     <scope>test</scope>
-                    <version>2.1.0</version>
                 </dependency>
                 <dependency>
                     <groupId>com.jayway.restassured</groupId>

Modified: james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java (original)
+++ james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java Mon Jan 18 11:42:07 2016
@@ -50,7 +50,10 @@ import org.junit.rules.RuleChain;
 import org.junit.rules.TemporaryFolder;
 
 import com.google.common.base.Charsets;
+import com.jayway.jsonpath.Configuration;
 import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.Option;
+import com.jayway.jsonpath.ParseContext;
 import com.jayway.restassured.RestAssured;
 import com.jayway.restassured.http.ContentType;
 
@@ -70,11 +73,15 @@ public abstract class GetMessagesMethodT
         .around(jmapServer);
 
     private AccessToken accessToken;
+    private ParseContext jsonPath;
 
     @Before
     public void setup() throws Exception {
         RestAssured.port = jmapServer.getPort();
         RestAssured.config = newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8));
+        jsonPath = JsonPath.using(Configuration.builder()
+                .options(Option.DEFAULT_PATH_LEAF_TO_NULL)
+                .build());
 
         String domain = "domain.tld";
         String username = "username@" + domain;
@@ -201,4 +208,79 @@ public abstract class GetMessagesMethodT
         assertThat(JsonPath.parse(response).<Map<String, String>>read(firstMessagePath + ".headers")).containsExactly(MapEntry.entry("subject", "my test subject"));
         assertThat(JsonPath.parse(response).<String>read(firstMessagePath + ".date")).isEqualTo("2014-10-30T14:12:00Z");
     }
+    
+    @Test
+    public void getMessagesShouldReturnFilteredPropertiesMessagesWhenAsked() throws Exception {
+        ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
+        jmapServer.serverProbe().appendMessage("username", new MailboxPath(MailboxConstants.USER_NAMESPACE, "username", "inbox"),
+                new ByteArrayInputStream("Subject: my test subject\r\n\r\ntestmail".getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+        
+        embeddedElasticSearch.awaitForElasticSearch();
+        
+        String response = given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"username-inbox-1\"], \"properties\": [\"id\", \"subject\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .content(startsWith("[[\"messages\","))
+            .extract()
+            .asString();
+
+        String firstResponsePath = "$.[0].[1]";
+        String firstMessagePath = firstResponsePath + ".list[0]";
+
+        assertThat(jsonPath.parse(response).<Integer>read("$.length()")).isEqualTo(1);
+        assertThat(jsonPath.parse(response).<Integer>read(firstResponsePath + ".list.length()")).isEqualTo(1);
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".id")).isEqualTo("username@domain.tld-inbox-1");
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".subject")).isEqualTo("my test subject");
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".textBody")).isNull();
+        assertThat(jsonPath.parse(response).<Boolean>read(firstMessagePath + ".isUnread")).isNull();
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".preview")).isNull();
+        assertThat(jsonPath.parse(response).<Map<String, String>>read(firstMessagePath + ".headers")).isNull();
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".date")).isNull();
+    }
+    
+    @Test
+    public void getMessagesShouldReturnFilteredHeaderPropertyWhenAsked() throws Exception {
+        ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
+        jmapServer.serverProbe().appendMessage("username", new MailboxPath(MailboxConstants.USER_NAMESPACE, "username", "inbox"),
+                new ByteArrayInputStream(("From: user@domain.tld\r\n"
+                        + "header1: Header1Content\r\n"
+                        + "HEADer2: Header2Content\r\n"
+                        + "Subject: my test subject\r\n"
+                        + "\r\n"
+                        + "testmail").getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+        
+        embeddedElasticSearch.awaitForElasticSearch();
+        
+        String response = given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"username-inbox-1\"], \"properties\": [\"headers.from\", \"headers.heADER2\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .content(startsWith("[[\"messages\","))
+            .extract()
+            .asString();
+
+        String firstResponsePath = "$.[0].[1]";
+        String firstMessagePath = firstResponsePath + ".list[0]";
+
+        assertThat(jsonPath.parse(response).<Integer>read("$.length()")).isEqualTo(1);
+        assertThat(jsonPath.parse(response).<Integer>read(firstResponsePath + ".list.length()")).isEqualTo(1);
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".id")).isEqualTo("username@domain.tld-inbox-1");
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".subject")).isNull();
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".textBody")).isNull();
+        assertThat(jsonPath.parse(response).<Boolean>read(firstMessagePath + ".isUnread")).isNull();
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".preview")).isNull();
+        assertThat(jsonPath.parse(response).<Map<String, String>>read(firstMessagePath + ".headers")).containsOnly(MapEntry.entry("from", "user@domain.tld"), MapEntry.entry("header2", "Header2Content"));
+        assertThat(jsonPath.parse(response).<String>read(firstMessagePath + ".date")).isNull();
+    }
 }

Modified: james/project/trunk/server/protocols/jmap/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/pom.xml?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/pom.xml (original)
+++ james/project/trunk/server/protocols/jmap/pom.xml Mon Jan 18 11:42:07 2016
@@ -251,6 +251,11 @@
                     <type>test-jar</type>
                 </dependency>
                 <dependency>
+                    <groupId>com.jayway.jsonpath</groupId>
+                    <artifactId>json-path</artifactId>
+                    <scope>test</scope>
+                </dependency>
+                <dependency>
                     <groupId>junit</groupId>
                     <artifactId>junit</artifactId>
                     <scope>test</scope>

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java Mon Jan 18 11:42:07 2016
@@ -19,9 +19,9 @@
 
 package org.apache.james.jmap.methods;
 
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -48,18 +48,22 @@ import org.apache.james.mailbox.store.ma
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.javatuples.Pair;
 
+import com.fasterxml.jackson.databind.ser.PropertyWriter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
 import com.github.fge.lambdas.Throwing;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 public class GetMessagesMethod<Id extends MailboxId> implements Method {
 
+    public static final String HEADERS_FILTER = "headersFilter";
     private static final Method.Request.Name METHOD_NAME = Method.Request.name("getMessages");
     private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("messages");
     private final MessageMapperFactory<Id> messageMapperFactory;
     private final MailboxMapperFactory<Id> mailboxMapperFactory;
-    
 
     @Inject
     @VisibleForTesting GetMessagesMethod(
@@ -85,18 +89,21 @@ public class GetMessagesMethod<Id extend
         Preconditions.checkNotNull(mailboxSession);
         Preconditions.checkArgument(request instanceof GetMessagesRequest);
         GetMessagesRequest getMessagesRequest = (GetMessagesRequest) request;
+        Optional<ImmutableSet<MessageProperty>> requestedProperties = getMessagesRequest.getProperties();
         return Stream.of(JmapResponse.builder().clientId(clientId)
-                            .response(getMessagesResponse(mailboxSession, getMessagesRequest))
+                            .response(getMessagesResponse(mailboxSession, getMessagesRequest, requestedProperties))
                             .responseName(RESPONSE_NAME)
-                            .properties(getMessagesRequest.getProperties().map(this::handleSpecificProperties))
+                            .properties(requestedProperties.map(this::handleSpecificProperties))
+                            .filterProvider(Optional.of(buildFilteringHeadersFilterProvider(requestedProperties)))
                             .build());
     }
 
     private Set<MessageProperty> handleSpecificProperties(Set<MessageProperty> input) {
-        Set<MessageProperty> toAdd = new HashSet<MessageProperty>();
-        Set<MessageProperty> toRemove = new HashSet<MessageProperty>();
+        Set<MessageProperty> toAdd = Sets.newHashSet();
+        Set<MessageProperty> toRemove = Sets.newHashSet();
         ensureContainsId(input, toAdd);
         handleBody(input, toAdd, toRemove);
+        handleHeadersProperties(input, toAdd, toRemove);
         return Sets.union(Sets.difference(input, toRemove), toAdd).immutableCopy();
     }
         
@@ -112,8 +119,48 @@ public class GetMessagesMethod<Id extend
             toRemove.add(MessageProperty.body);
         }
     }
+    
+    private void handleHeadersProperties(Set<MessageProperty> input, Set<MessageProperty> toAdd, Set<MessageProperty> toRemove) {
+        Set<MessageProperty> selectHeadersProperties = MessageProperty.selectHeadersProperties(input);
+        if (!selectHeadersProperties.isEmpty()) {
+            toAdd.add(MessageProperty.headers);
+            toRemove.addAll(selectHeadersProperties);
+        }
+    }
+    
+    private SimpleFilterProvider buildFilteringHeadersFilterProvider(Optional<ImmutableSet<MessageProperty>> requestedProperties) {
+        Set<MessageProperty> selectedHeadersProperties = requestedProperties
+                .map(MessageProperty::selectHeadersProperties)
+                .orElse(ImmutableSet.of());
+
+        return new SimpleFilterProvider()
+                .addFilter(HEADERS_FILTER, buildPropertyFilter(selectedHeadersProperties))
+                .addFilter(JmapResponseWriterImpl.PROPERTIES_FILTER, SimpleBeanPropertyFilter.serializeAll());
+    }
+    
+    private SimpleBeanPropertyFilter buildPropertyFilter(Set<MessageProperty> propertiesToInclude) {
+        if (propertiesToInclude.isEmpty()) {
+            return SimpleBeanPropertyFilter.serializeAll();
+        } else {
+            return new IncludeMessagePropertyPropertyFilter(propertiesToInclude);
+        }
+    }
+    
+    private static class IncludeMessagePropertyPropertyFilter extends SimpleBeanPropertyFilter {
+        private Set<MessageProperty> propertiesToInclude;
 
-    private GetMessagesResponse getMessagesResponse(MailboxSession mailboxSession, GetMessagesRequest getMessagesRequest) {
+        public IncludeMessagePropertyPropertyFilter(Set<MessageProperty> propertiesToInclude) {
+            this.propertiesToInclude = propertiesToInclude;
+        }
+        
+        @Override
+        protected boolean include(PropertyWriter writer) {
+            String currentProperty = writer.getName();
+            return propertiesToInclude.contains(MessageProperty.headerValueOf(currentProperty));
+        }
+    }
+    
+    private GetMessagesResponse getMessagesResponse(MailboxSession mailboxSession, GetMessagesRequest getMessagesRequest, Optional<ImmutableSet<MessageProperty>> requestedProperties) {
         getMessagesRequest.getAccountId().ifPresent(GetMessagesMethod::notImplemented);
         
         Function<MessageId, Stream<Pair<MailboxMessage<Id>, MailboxPath>>> loadMessages = loadMessage(mailboxSession);

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponse.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponse.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponse.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponse.java Mon Jan 18 11:42:07 2016
@@ -25,6 +25,7 @@ import java.util.Set;
 import org.apache.james.jmap.model.ClientId;
 import org.apache.james.jmap.model.Property;
 
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
 import com.google.common.annotations.VisibleForTesting;
 
 public class JmapResponse {
@@ -39,6 +40,7 @@ public class JmapResponse {
         private ClientId id;
         private Method.Response response;
         private Optional<? extends Set<? extends Property>> properties = Optional.empty();
+        private Optional<SimpleFilterProvider> filterProvider = Optional.empty();
 
         private Builder() {
         }
@@ -67,6 +69,11 @@ public class JmapResponse {
             this.properties = Optional.ofNullable(properties);
             return this;
         }
+        
+        public Builder filterProvider(Optional<SimpleFilterProvider> filterProvider) {
+            this.filterProvider = filterProvider;
+            return this;
+        }
 
         public Builder error() {
             return error(DEFAULT_ERROR_MESSAGE);
@@ -80,7 +87,7 @@ public class JmapResponse {
 
         
         public JmapResponse build() {
-            return new JmapResponse(responseName, id, response, properties);
+            return new JmapResponse(responseName, id, response, properties, filterProvider);
         }
     }
 
@@ -105,12 +112,14 @@ public class JmapResponse {
     private final ClientId clientId;
     private final Method.Response response;
     private Optional<? extends Set<? extends Property>> properties;
+    private Optional<SimpleFilterProvider> filterProvider;
     
-    private JmapResponse(Method.Response.Name method, ClientId clientId, Method.Response response, Optional<? extends Set<? extends Property>> properties) {
+    private JmapResponse(Method.Response.Name method, ClientId clientId, Method.Response response, Optional<? extends Set<? extends Property>> properties, Optional<SimpleFilterProvider> filterProvider) {
         this.method = method;
         this.clientId = clientId;
         this.response = response;
         this.properties = properties;
+        this.filterProvider = filterProvider;
     }
 
     public Method.Response.Name getResponseName() {
@@ -128,4 +137,8 @@ public class JmapResponse {
     public Optional<? extends Set<? extends Property>> getProperties() {
         return properties;
     }
+
+    public Optional<SimpleFilterProvider> getFilterProvider() {
+        return filterProvider;
+    }
 }

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponseWriterImpl.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponseWriterImpl.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponseWriterImpl.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/JmapResponseWriterImpl.java Mon Jan 18 11:42:07 2016
@@ -39,6 +39,7 @@ import com.fasterxml.jackson.databind.se
 
 public class JmapResponseWriterImpl implements JmapResponseWriter {
 
+    public static final String PROPERTIES_FILTER = "propertiesFilter";
     private final Set<Module> jacksonModules;
 
     @Inject
@@ -59,20 +60,25 @@ public class JmapResponseWriterImpl impl
     }
     
     private ObjectMapper newConfiguredObjectMapper(JmapResponse jmapResponse) {
-        ObjectMapper objectMapper = new ObjectMapper().registerModules(jacksonModules)
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.registerModules(jacksonModules)
                 .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
-
-        objectMapper.setFilterProvider(buildPropertiesFilter(jmapResponse.getProperties()));
+        
+        FilterProvider filterProvider = jmapResponse
+                .getFilterProvider()
+                .orElseGet(SimpleFilterProvider::new)
+                .addFilter(PROPERTIES_FILTER, getPropertiesFilter(jmapResponse.getProperties()));
+        
+        objectMapper.setFilterProvider(filterProvider);
 
         return objectMapper;
     }
-
-    private FilterProvider buildPropertiesFilter(Optional<? extends Set<? extends Property>> properties) {
-        PropertyFilter filter = properties
+    
+    private PropertyFilter getPropertiesFilter(Optional<? extends Set<? extends Property>> properties) {
+        return properties
                 .map(this::toFieldNames)
                 .map(SimpleBeanPropertyFilter::filterOutAllExcept)
                 .orElse(SimpleBeanPropertyFilter.serializeAll());
-        return new SimpleFilterProvider().addFilter("propertiesFilter", filter);
     }
     
     private Set<String> toFieldNames(Set<? extends Property> properties) {

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java Mon Jan 18 11:42:07 2016
@@ -30,6 +30,8 @@ import java.util.stream.Collectors;
 
 import com.fasterxml.jackson.annotation.JsonFilter;
 import org.apache.commons.lang.NotImplementedException;
+import org.apache.james.jmap.methods.GetMessagesMethod;
+import org.apache.james.jmap.methods.JmapResponseWriterImpl;
 import org.apache.james.jmap.model.message.EMailer;
 import org.apache.james.jmap.model.message.IndexableMessage;
 import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
@@ -46,7 +48,7 @@ import com.google.common.collect.Multima
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 
 @JsonDeserialize(builder = Message.Builder.class)
-@JsonFilter("propertiesFilter")
+@JsonFilter(JmapResponseWriterImpl.PROPERTIES_FILTER)
 public class Message {
     public static final String NO_SUBJECT = "(No subject)";
     public static final String MULTIVALUED_HEADERS_SEPARATOR = ", ";
@@ -343,6 +345,7 @@ public class Message {
     private final boolean isAnswered;
     private final boolean isDraft;
     private final boolean hasAttachment;
+    @JsonFilter(GetMessagesMethod.HEADERS_FILTER)
     private final ImmutableMap<String, String> headers;
     private final Optional<Emailer> from;
     private final ImmutableList<Emailer> to;

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperty.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperty.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperty.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperty.java Mon Jan 18 11:42:07 2016
@@ -18,47 +18,89 @@
  ****************************************************************/
 package org.apache.james.jmap.model;
 
-import com.google.common.collect.ImmutableSet;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
 
-public enum MessageProperty implements Property {
-    id("id"),
-    blobId("blobId"),
-    threadId("threadId"),
-    mailboxIds("mailboxIds"),
-    inReplyToMessageId("inReplyToMessageId"),
-    isUnread("isUnread"),
-    isFlagged("isFlagged"),
-    isAnswered("isAnswered"),
-    isDraft("isDraft"),
-    hasAttachment("hasAttachment"),
-    headers("headers"),
-    from("from"),
-    to("to"),
-    cc("cc"),
-    bcc("bcc"),
-    replyTo("replyTo"),
-    subject("subject"),
-    date("date"),
-    size("size"),
-    preview("preview"),
-    textBody("textBody"),
-    htmlBody("htmlBody"),
-    attachments("attachments"),
-    attachedMessages("attachedMessages"),
-    body("body"),
-    headers_property("headers.property");
+import org.apache.james.util.streams.Collectors;
+
+import com.google.common.base.Preconditions;
+
+public class MessageProperty implements Property {
+    public static MessageProperty id = valueOf("id");
+    public static MessageProperty blobId = valueOf("blobId");
+    public static MessageProperty threadId = valueOf("threadId");
+    public static MessageProperty mailboxIds = valueOf("mailboxIds");
+    public static MessageProperty inReplyToMessageId = valueOf("inReplyToMessageId");
+    public static MessageProperty isUnread = valueOf("isUnread");
+    public static MessageProperty isFlagged = valueOf("isFlagged");
+    public static MessageProperty isAnswered = valueOf("isAnswered");
+    public static MessageProperty isDraft = valueOf("isDraft");
+    public static MessageProperty hasAttachment = valueOf("hasAttachment");
+    public static MessageProperty headers = valueOf("headers");
+    public static MessageProperty from = valueOf("from");
+    public static MessageProperty to = valueOf("to");
+    public static MessageProperty cc = valueOf("cc");
+    public static MessageProperty bcc = valueOf("bcc");
+    public static MessageProperty replyTo = valueOf("replyTo");
+    public static MessageProperty subject = valueOf("subject");
+    public static MessageProperty date = valueOf("date");
+    public static MessageProperty size = valueOf("size");
+    public static MessageProperty preview = valueOf("preview");
+    public static MessageProperty textBody = valueOf("textBody");
+    public static MessageProperty htmlBody = valueOf("htmlBody");
+    public static MessageProperty attachments = valueOf("attachments");
+    public static MessageProperty attachedMessages = valueOf("attachedMessages");
+    public static MessageProperty body = valueOf("body");
+    
+    private static String HEADER_PROPERTY_PREFIX = "headers.";
     
     private String property;
+    
+    private MessageProperty(String property) {
+        this.property = property.toLowerCase(Locale.US);
+    }
 
-    MessageProperty(String property) {
-        this.property = property;
+    public static MessageProperty valueOf(String property) {
+        return new MessageProperty(property);
+    }
+    
+    public static MessageProperty headerValueOf(String headerProperty) {
+        Preconditions.checkNotNull(headerProperty);
+        return new MessageProperty(HEADER_PROPERTY_PREFIX + headerProperty);
     }
     
+    public static Set<MessageProperty> selectHeadersProperties(Set<MessageProperty> properties) {
+        return properties.stream()
+                .filter(MessageProperty::isHeaderProperty)
+                .collect(Collectors.toImmutableSet());
+    }
+
+    @Override
     public String asFieldName() {
         return property;
     }
     
-    public static ImmutableSet<MessageProperty> all() {
-        return ImmutableSet.copyOf(values());
+    public boolean isHeaderProperty() {
+        return property.startsWith(HEADER_PROPERTY_PREFIX);
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MessageProperty) {
+            MessageProperty other = (MessageProperty) obj;
+            return Objects.equals(this.property, other.property);
+        }
+        return false;
+    }
+    
+    @Override
+    public int hashCode() {
+        return Objects.hash(property);
+    }
+    
+    @Override
+    public String toString() {
+        return Objects.toString(property);
     }
 }

Modified: james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java?rev=1725240&r1=1725239&r2=1725240&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java (original)
+++ james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java Mon Jan 18 11:42:07 2016
@@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -46,14 +47,18 @@ import org.apache.james.mailbox.inmemory
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.MockAuthenticator;
 import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.assertj.core.api.Condition;
+import org.assertj.core.data.MapEntry;
 import org.assertj.core.groups.Tuple;
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
+import com.jayway.jsonpath.JsonPath;
 
 public class GetMessagesMethodTest {
 
@@ -235,4 +240,58 @@ public class GetMessagesMethodTest {
             .asList()
             .containsOnly(MessageProperty.id, MessageProperty.textBody);
     }
+    
+    @Test
+    public void processShouldReturnHeadersFieldWhenSpecificHeadersRequestedInPropertyList() throws MailboxException {
+        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
+        Date now = new Date();
+        ByteArrayInputStream message1Content = new ByteArrayInputStream(("From: user@domain.tld\r\n"
+                + "header1: Header1Content\r\n"
+                + "HEADer2: Header2Content\r\n"
+                + "Subject: message 1 subject\r\n\r\nmy message").getBytes(Charsets.UTF_8));
+        long message1Uid = inbox.appendMessage(message1Content, now, session, false, null);
+        
+        GetMessagesRequest request = GetMessagesRequest.builder()
+                .ids(new MessageId(ROBERT, inboxPath, message1Uid))
+                .properties(MessageProperty.valueOf("headers.from"), MessageProperty.valueOf("headers.heADER2"))
+                .build();
+
+        GetMessagesMethod<InMemoryId> testee = new GetMessagesMethod<>(mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
+
+        assertThat(result)
+            .hasSize(1)
+            .extracting(JmapResponse::getProperties)
+            .flatExtracting(Optional::get)
+            .asList()
+            .containsOnly(MessageProperty.id, MessageProperty.headers);
+    }
+    
+    @Test
+    public void processShouldReturnAPreconfiguredObjectMapperFilteringHeaders() throws Exception {
+        MessageManager inbox = mailboxManager.getMailbox(inboxPath, session);
+        Date now = new Date();
+        ByteArrayInputStream message1Content = new ByteArrayInputStream(("From: user@domain.tld\r\n"
+                + "header1: Header1Content\r\n"
+                + "HEADer2: Header2Content\r\n"
+                + "Subject: message 1 subject\r\n\r\nmy message").getBytes(Charsets.UTF_8));
+        long message1Uid = inbox.appendMessage(message1Content, now, session, false, null);
+        
+        GetMessagesRequest request = GetMessagesRequest.builder()
+                .ids(new MessageId(ROBERT, inboxPath, message1Uid))
+                .properties(MessageProperty.valueOf("headers.from"), MessageProperty.valueOf("headers.heADER2"))
+                .build();
+
+        GetMessagesMethod<InMemoryId> testee = new GetMessagesMethod<>(mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
+
+        assertThat(result)
+            .hasSize(1)
+            .extracting(JmapResponse::getFilterProvider)
+            .are(new Condition<>(Optional::isPresent, "present"));
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setFilterProvider(result.get(0).getFilterProvider().get());
+        String response = objectMapper.writer().writeValueAsString(result.get(0));
+        assertThat(JsonPath.parse(response).<Map<String, String>>read("$.response.list[0].headers")).containsOnly(MapEntry.entry("from", "user@domain.tld"), MapEntry.entry("header2", "Header2Content"));
+    }
 }

Added: james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertyTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertyTest.java?rev=1725240&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertyTest.java (added)
+++ james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertyTest.java Mon Jan 18 11:42:07 2016
@@ -0,0 +1,95 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+public class MessagePropertyTest {
+    
+    @Test(expected=NullPointerException.class)
+    public void valueOfShouldThrowWhenNull() {
+        MessageProperty.valueOf(null);
+    }
+    
+    @Test
+    public void valueOfThenAsFieldNameShouldReturnLowerCasedProperty() {
+        assertThat(MessageProperty.valueOf("ProP").asFieldName()).isEqualTo("prop");
+    }
+    
+    @Test(expected=NullPointerException.class)
+    public void headerValueOfShouldThrowWhenNull() {
+        MessageProperty.headerValueOf(null);
+    }
+    
+    @Test
+    public void headerValueOfShouldReturnPrefixedProperty() {
+        assertThat(MessageProperty.headerValueOf("Prop").asFieldName()).isEqualTo("headers.prop");
+    }
+    
+    @Test
+    public void isHeaderPropertyShouldReturnFalseWhenNotPrefixed() {
+        assertThat(MessageProperty.valueOf("prop").isHeaderProperty()).isFalse();
+    }
+    
+    @Test
+    public void isHeaderPropertyShouldReturnFalseWhenPrefixedBySomethingElse() {
+        assertThat(MessageProperty.valueOf("somethingelse.prop").isHeaderProperty()).isFalse();
+    }
+    
+    @Test
+    public void isHeaderPropertyShouldReturnTrueWhenPrefixedByHeaders() {
+        assertThat(MessageProperty.valueOf("headers.prop").isHeaderProperty()).isTrue();
+    }
+    
+    @Test(expected=NullPointerException.class)
+    public void selectHeadersPropertiesShouldThrowWhenNull() {
+        MessageProperty.selectHeadersProperties(null);
+    }
+    
+    @Test
+    public void selectHeadersPropertiesShouldReturnOnlyHeadersProperties() {
+        Set<MessageProperty> properties = ImmutableSet.of(
+                MessageProperty.from,
+                MessageProperty.valueOf("headers.prop"),
+                MessageProperty.valueOf("headers.prop2"),
+                MessageProperty.valueOf("prop"));
+        assertThat(MessageProperty.selectHeadersProperties(properties)).containsOnly(MessageProperty.headerValueOf("prop"), MessageProperty.headerValueOf("prop2"));
+    }
+    
+    @Test
+    public void equalsShouldBeTrueWhenIdenticalProperties() {
+        assertThat(MessageProperty.valueOf("prop")).isEqualTo(MessageProperty.valueOf("prop"));
+    }
+
+    @Test
+    public void equalsShouldBeFalseWhenDifferentProperties() {
+        assertThat(MessageProperty.valueOf("prop")).isNotEqualTo(MessageProperty.valueOf("other"));
+    }
+
+    @Test
+    public void equalsShouldBeTrueWhenDifferentCaseProperties() {
+        assertThat(MessageProperty.valueOf("prOP")).isEqualTo(MessageProperty.valueOf("PRop"));
+    }
+}




---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org