Skip to content

Commit

Permalink
JAMES-1662 implement properties filtering on JMAP getMailboxes
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/james/project/trunk@1725995 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
mbaechler committed Jan 21, 2016
1 parent 19e4802 commit 237c790
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 36 deletions.
Expand Up @@ -120,20 +120,6 @@ public void getMailboxesShouldErrorNotSupportedWhenRequestContainsNonNullIds() t
.content(equalTo("[[\"error\",{\"type\":\"Not yet implemented\"},\"#0\"]]"));
}

@Test
public void getMailboxesShouldErrorNotSupportedWhenRequestContainsNonNullProperties() throws Exception {
given()
.accept(ContentType.JSON)
.contentType(ContentType.JSON)
.header("Authorization", accessToken.serialize())
.body("[[\"getMailboxes\", {\"properties\": []}, \"#0\"]]")
.when()
.post("/jmap")
.then()
.statusCode(200)
.content(equalTo("[[\"error\",{\"type\":\"Not yet implemented\"},\"#0\"]]"));
}

@Test
public void getMailboxesShouldErrorInvalidArgumentsWhenRequestIsInvalid() throws Exception {
given()
Expand Down Expand Up @@ -252,6 +238,88 @@ public void getMailboxesShouldReturnMailboxesWhenAvailable() throws Exception {
assertThat(jsonPath.parse(response).<Integer>read(firstMailboxPath + ".unreadThreads")).isEqualTo(0);
}

@Test
public void getMailboxesShouldReturnFilteredMailboxesPropertiesWhenRequestContainsFilterProperties() throws Exception {
String user = "user";
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, user, "name");

String response = given()
.accept(ContentType.JSON)
.contentType(ContentType.JSON)
.header("Authorization", accessToken.serialize())
.body("[[\"getMailboxes\", {\"properties\" : [\"unreadMessages\", \"sortOrder\"]}, \"#0\"]]")
.when()
.post("/jmap")
.then()
.statusCode(200)
.content(startsWith("[[\"mailboxes\","))
.extract()
.asString();

String firstMailboxPath = "$.[0].[1].list.[0]";
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".id")).isNotEmpty();
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".name")).isNull();
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".parentId")).isNull();
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".role")).isNull();
assertThat(jsonPath.parse(response).<Integer>read(firstMailboxPath + ".sortOrder")).isEqualTo(1000);
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mustBeOnlyMailbox")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayReadItems")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayAddItems")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayRemoveItems")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayCreateChild")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayRename")).isNull();
assertThat(jsonPath.parse(response).<Boolean>read(firstMailboxPath + ".mayDelete")).isNull();
assertThat(jsonPath.parse(response).<Integer>read(firstMailboxPath + ".totalMessages")).isNull();
assertThat(jsonPath.parse(response).<Integer>read(firstMailboxPath + ".unreadMessages")).isEqualTo(0);
assertThat(jsonPath.parse(response).<Integer>read(firstMailboxPath + ".unreadThreads")).isNull();
}

@Test
public void getMailboxesShouldReturnIdWhenRequestContainsEmptyPropertyListFilter() throws Exception {
String user = "user";
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, user, "name");

String response = given()
.accept(ContentType.JSON)
.contentType(ContentType.JSON)
.header("Authorization", accessToken.serialize())
.body("[[\"getMailboxes\", {\"properties\" : []}, \"#0\"]]")
.when()
.post("/jmap")
.then()
.statusCode(200)
.content(startsWith("[[\"mailboxes\","))
.extract()
.asString();

String firstMailboxPath = "$.[0].[1].list.[0]";
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".id")).isNotEmpty();
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".name")).isNull();
}

@Test
public void getMailboxesShouldIgnoreUnknownPropertiesWhenRequestContainsUnknownPropertyListFilter() throws Exception {
String user = "user";
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, user, "name");

String response = given()
.accept(ContentType.JSON)
.contentType(ContentType.JSON)
.header("Authorization", accessToken.serialize())
.body("[[\"getMailboxes\", {\"properties\" : [\"unknown\"]}, \"#0\"]]")
.when()
.post("/jmap")
.then()
.statusCode(200)
.content(startsWith("[[\"mailboxes\","))
.extract()
.asString();

String firstMailboxPath = "$.[0].[1].list.[0]";
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".id")).isNotEmpty();
assertThat(jsonPath.parse(response).<String>read(firstMailboxPath + ".name")).isNull();
}

@SuppressWarnings("unchecked")
@Test
public void getMailboxesShouldReturnMailboxesWithSortOrder() throws Exception {
Expand Down
Expand Up @@ -20,13 +20,17 @@
package org.apache.james.jmap.methods;

import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import javax.inject.Inject;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.james.jmap.model.ClientId;
import org.apache.james.jmap.model.GetMailboxesRequest;
import org.apache.james.jmap.model.GetMailboxesResponse;
import org.apache.james.jmap.model.MailboxProperty;
import org.apache.james.jmap.model.mailbox.Mailbox;
import org.apache.james.jmap.model.mailbox.Role;
import org.apache.james.jmap.model.mailbox.SortOrder;
Expand Down Expand Up @@ -73,13 +77,19 @@ public Class<? extends JmapRequest> requestType() {

public Stream<JmapResponse> process(JmapRequest request, ClientId clientId, MailboxSession mailboxSession) {
Preconditions.checkArgument(request instanceof GetMailboxesRequest);
GetMailboxesRequest mailboxesRequest = (GetMailboxesRequest) request;
return Stream.of(
JmapResponse.builder().clientId(clientId)
.response(getMailboxesResponse(mailboxSession))
.properties(mailboxesRequest.getProperties().map(this::ensureContainsId))
.responseName(RESPONSE_NAME)
.build());
}

private Set<MailboxProperty> ensureContainsId(Set<MailboxProperty> input) {
return Sets.union(input, ImmutableSet.of(MailboxProperty.ID)).immutableCopy();
}

private GetMailboxesResponse getMailboxesResponse(MailboxSession mailboxSession) {
GetMailboxesResponse.Builder builder = GetMailboxesResponse.builder();
try {
Expand Down
Expand Up @@ -27,6 +27,7 @@

import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;

public class JmapResponse {

Expand Down Expand Up @@ -61,13 +62,12 @@ public Builder response(Method.Response response) {
}

public Builder properties(Optional<? extends Set<? extends Property>> properties) {
this.properties = properties;
this.properties = properties.map(ImmutableSet::copyOf);
return this;
}

public Builder properties(Set<? extends Property> properties) {
this.properties = Optional.ofNullable(properties);
return this;
return properties(Optional.ofNullable(properties));
}

public Builder filterProvider(Optional<SimpleFilterProvider> filterProvider) {
Expand Down
Expand Up @@ -18,15 +18,16 @@
****************************************************************/
package org.apache.james.jmap.model;

import java.util.List;
import java.util.Optional;

import org.apache.commons.lang.NotImplementedException;
import org.apache.james.jmap.methods.JmapRequest;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.NotImplementedException;
import org.apache.james.jmap.methods.JmapRequest;
import org.apache.james.util.streams.Collectors;

import java.util.List;
import java.util.Optional;

@JsonDeserialize(builder = GetMailboxesRequest.Builder.class)
public class GetMailboxesRequest implements JmapRequest {
Expand All @@ -40,11 +41,11 @@ public static class Builder {

private String accountId;
private ImmutableList.Builder<String> ids;
private ImmutableList.Builder<String> properties;
private Optional<ImmutableSet<MailboxProperty>> properties;

private Builder() {
ids = ImmutableList.builder();
properties = ImmutableList.builder();
properties = Optional.empty();
}

public Builder accountId(String accountId) {
Expand All @@ -62,22 +63,25 @@ public Builder ids(List<String> ids) {
}

public Builder properties(List<String> properties) {
if (properties != null) {
throw new NotImplementedException();
}
this.properties = Optional.of(
properties.stream()
.map(MailboxProperty::findProperty)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toImmutableSet()));
return this;
}

public GetMailboxesRequest build() {
return new GetMailboxesRequest(Optional.ofNullable(accountId), ids.build(), properties.build());
return new GetMailboxesRequest(Optional.ofNullable(accountId), ids.build(), properties);
}
}

private final Optional<String> accountId;
private final List<String> ids;
private final List<String> properties;
private final Optional<ImmutableSet<MailboxProperty>> properties;

private GetMailboxesRequest(Optional<String> accountId, List<String> ids, List<String> properties) {
private GetMailboxesRequest(Optional<String> accountId, List<String> ids, Optional<ImmutableSet<MailboxProperty>> properties) {
this.accountId = accountId;
this.ids = ids;
this.properties = properties;
Expand All @@ -91,7 +95,7 @@ public List<String> getIds() {
return ids;
}

public List<String> getProperties() {
public Optional<ImmutableSet<MailboxProperty>> getProperties() {
return properties;
}
}
@@ -0,0 +1,60 @@
/****************************************************************
* 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 com.google.common.base.Preconditions;

import java.util.Arrays;
import java.util.Optional;

public enum MailboxProperty implements Property {
ID("id"),
NAME("name"),
PARENT_ID("parentId"),
ROLE("role"),
SORT_ORDER("sortOrder"),
MUST_BE_ONLY_MAILBOX("mustBeOnlyMailbox"),
MAY_READ_ITEMS("mayReadItems"),
MAY_ADD_ITEMS("mayAddItems"),
MAY_REMOVE_ITEMS("mayRemoveItems"),
MAY_CREATE_CHILD("mayCreateChild"),
MAY_RENAME("mayRename"),
MAY_DELETE("mayDelete"),
TOTAL_MESSAGES("totalMessages"),
UNREAD_MESSAGES("unreadMessages"),
TOTAL_THREADS("totalThreads"),
UNREAD_THREADS("unreadThreads");

private final String fieldName;

MailboxProperty(String fieldName) {
this.fieldName = fieldName;
}

public String asFieldName() {
return fieldName;
}

public static Optional<MailboxProperty> findProperty(String value) {
Preconditions.checkNotNull(value);
return Arrays.stream(values())
.filter(element -> element.fieldName.equals(value))
.findAny();
}
}
Expand Up @@ -22,13 +22,16 @@
import java.util.Objects;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.apache.james.jmap.methods.JmapResponseWriterImpl;

@JsonDeserialize(builder = Mailbox.Builder.class)
@JsonFilter(JmapResponseWriterImpl.PROPERTIES_FILTER)
public class Mailbox {

public static Builder builder() {
Expand Down
Expand Up @@ -36,8 +36,4 @@ public void builderShouldThrowWhenIds() {
GetMailboxesRequest.builder().ids(ImmutableList.of());
}

@Test(expected=NotImplementedException.class)
public void builderShouldThrowWhenProperties() {
GetMailboxesRequest.builder().properties(ImmutableList.of());
}
}
@@ -0,0 +1,46 @@
/****************************************************************
* 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 org.junit.Test;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class MailboxPropertyTest {

@Test
public void findPropertyShouldReturnEmptyWhenNoEnumEntryMatchGivenString() {
assertThat(MailboxProperty.findProperty("should not match any entry")).isEmpty();
}

@Test
public void findPropertyShouldThrowWhenNullString() {
assertThatThrownBy(() -> MailboxProperty.findProperty(null))
.isInstanceOf(NullPointerException.class);
}

@Test
public void findPropertyShouldReturnMatchingEnumEntryWhenExistingValue() {
assertThat(MailboxProperty.findProperty("name")).isEqualTo(Optional.of(MailboxProperty.NAME));
}

}

0 comments on commit 237c790

Please sign in to comment.