This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 102b8db50d2a08a15e713291590e492e41b34b48 Author: Tran Tien Duc <dt...@linagora.com> AuthorDate: Thu Mar 7 11:50:01 2019 +0700 JAMES-2663 QueryDTO with deserializer and translator --- .../java/org/apache/james/vault/search/Query.java | 4 + .../webadmin-mailbox-deleted-message-vault/pom.xml | 10 + .../webadmin/vault/routes/query/CriterionDTO.java | 88 ++++++++ .../webadmin/vault/routes/query/QueryDTO.java | 71 +++++++ .../webadmin/vault/routes/query/QueryElement.java | 31 +-- .../routes/query/QueryElementDeserializer.java | 51 +++++ .../vault/routes/query/QueryTranslator.java | 225 +++++++++++++++++++++ .../routes/query/QueryElementDeserializerTest.java | 89 ++++++++ .../vault/routes/query/QueryElementTest.java | 36 ++-- .../vault/routes/query/QueryTranslatorTest.java | 67 ++++++ 10 files changed, 622 insertions(+), 50 deletions(-) diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java index 039fd52..8595a43 100644 --- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java +++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java @@ -30,6 +30,10 @@ public class Query { public static final Query ALL = new Query(ImmutableList.of()); private static final Predicate<DeletedMessage> MATCH_ALL = any -> true; + public static Query of(List<Criterion> criteria) { + return new Query(criteria); + } + public static Query of(Criterion... criteria) { return new Query(ImmutableList.copyOf(criteria)); } diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml index aa6847d..2c06be8 100644 --- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml @@ -105,6 +105,16 @@ <scope>test</scope> </dependency> <dependency> + <groupId>net.javacrumbs.json-unit</groupId> + <artifactId>json-unit-assertj</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>nl.jqno.equalsverifier</groupId> + <artifactId>equalsverifier</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/CriterionDTO.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/CriterionDTO.java new file mode 100644 index 0000000..547d3c0 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/CriterionDTO.java @@ -0,0 +1,88 @@ +/**************************************************************** + * 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.vault.routes.query; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; + +@JsonDeserialize(as = CriterionDTO.class) +public class CriterionDTO implements QueryElement { + + @VisibleForTesting + static CriterionDTO from(QueryTranslator.FieldName fieldName, QueryTranslator.Operator operator, String value) { + return new CriterionDTO(fieldName.getValue(), operator.getValue(), value); + } + + private final String fieldName; + private final String operator; + private final String value; + + @JsonCreator + public CriterionDTO(@JsonProperty("fieldName") String fieldName, + @JsonProperty("operator") String operator, + @JsonProperty("value") String value) { + this.fieldName = fieldName; + this.operator = operator; + this.value = value; + } + + public String getFieldName() { + return fieldName; + } + + public String getOperator() { + return operator; + } + + public String getValue() { + return value; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof CriterionDTO) { + CriterionDTO that = (CriterionDTO) o; + + return Objects.equals(this.fieldName, that.getFieldName()) + && Objects.equals(this.operator, that.getOperator()) + && Objects.equals(this.value, that.getValue()); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(fieldName, operator, value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("fieldName", fieldName) + .add("operator", operator) + .add("value", value) + .toString(); + } +} \ No newline at end of file diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryDTO.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryDTO.java new file mode 100644 index 0000000..496811b --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryDTO.java @@ -0,0 +1,71 @@ +/**************************************************************** + * 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.vault.routes.query; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +@JsonDeserialize(as = QueryDTO.class) +public class QueryDTO implements QueryElement { + + @VisibleForTesting + static QueryDTO and(QueryElement... queryElements) { + return new QueryDTO(QueryTranslator.Combinator.AND.getValue(), ImmutableList.copyOf(queryElements)); + } + + private final String combinator; + private final List<QueryElement> criteria; + + @JsonCreator + public QueryDTO(@JsonProperty("combinator") String combinator, @JsonProperty("criteria") List<QueryElement> criteria) { + this.combinator = combinator; + this.criteria = criteria; + } + + public String getCombinator() { + return combinator; + } + + public List<QueryElement> getCriteria() { + return criteria; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof QueryDTO) { + QueryDTO queryDTO = (QueryDTO) o; + + return Objects.equals(this.combinator, queryDTO.getCombinator()) + && Objects.equals(this.criteria, queryDTO.getCriteria()); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(combinator, criteria); + } +} diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElement.java similarity index 59% copy from mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java copy to server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElement.java index 039fd52..2c0e9e1 100644 --- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElement.java @@ -17,33 +17,10 @@ * under the License. * ****************************************************************/ -package org.apache.james.vault.search; +package org.apache.james.webadmin.vault.routes.query; -import java.util.List; -import java.util.function.Predicate; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.apache.james.vault.DeletedMessage; - -import com.google.common.collect.ImmutableList; - -public class Query { - public static final Query ALL = new Query(ImmutableList.of()); - private static final Predicate<DeletedMessage> MATCH_ALL = any -> true; - - public static Query of(Criterion... criteria) { - return new Query(ImmutableList.copyOf(criteria)); - } - - private final List<Criterion> criteria; - - private Query(List<Criterion> criteria) { - this.criteria = criteria; - } - - public Predicate<DeletedMessage> toPredicate() { - return criteria.stream() - .map(Criterion::toPredicate) - .reduce(Predicate::and) - .orElse(MATCH_ALL); - } +@JsonDeserialize(using = QueryElementDeserializer.class) +interface QueryElement { } diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializer.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializer.java new file mode 100644 index 0000000..ad608bb --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializer.java @@ -0,0 +1,51 @@ +/**************************************************************** + * 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.vault.routes.query; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ObjectNode; + +class QueryElementDeserializer extends StdDeserializer<QueryElement> { + protected QueryElementDeserializer() { + super(QueryElement.class); + } + + @Override + public QueryElement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec(); + ObjectNode queryElement = mapper.readTree(jsonParser); + + if (isComposedElement(queryElement)) { + return mapper.treeToValue(queryElement, QueryDTO.class); + } + return mapper.treeToValue(queryElement, CriterionDTO.class); + } + + private boolean isComposedElement(ObjectNode queryElement) { + return queryElement.findValue("combinator") != null + || queryElement.findValue("criteria") != null; + } +} diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryTranslator.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryTranslator.java new file mode 100644 index 0000000..33067d6 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/query/QueryTranslator.java @@ -0,0 +1,225 @@ +/**************************************************************** + * 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.vault.routes.query; + +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.DELETION_DATE; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.DELIVERY_DATE; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.HAS_ATTACHMENT; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.ORIGIN_MAILBOXES; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.RECIPIENTS; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.SENDER; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName.SUBJECT; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldValueParser.BOOLEAN_PARSER; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldValueParser.MAIL_ADDRESS_PARSER; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldValueParser.STRING_PARSER; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldValueParser.ZONED_DATE_TIME_PARSER; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.AFTER_OR_EQUALS; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.BEFORE_OR_EQUALS; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.CONTAINS; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.CONTAINS_IGNORE_CASE; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.EQUALS; +import static org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator.EQUALS_IGNORE_CASE; + +import java.time.ZonedDateTime; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.inject.Inject; +import javax.mail.internet.AddressException; + +import org.apache.james.core.MailAddress; +import org.apache.james.mailbox.model.MailboxId; +import org.apache.james.vault.search.Criterion; +import org.apache.james.vault.search.CriterionFactory; +import org.apache.james.vault.search.Query; + +import com.github.fge.lambdas.Throwing; +import com.github.steveash.guavate.Guavate; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableTable; + +public class QueryTranslator { + + public static class QueryTranslatorException extends RuntimeException { + QueryTranslatorException(String message) { + super(message); + } + } + + enum Combinator { + AND("and"); + + private final String value; + + Combinator(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + enum FieldName { + DELETION_DATE("deletionDate"), + DELIVERY_DATE("deliveryDate"), + RECIPIENTS("recipients"), + SENDER("sender"), + HAS_ATTACHMENT("hasAttachment"), + ORIGIN_MAILBOXES("originMailboxes"), + SUBJECT("subject"); + + static FieldName getField(String fieldNameString) throws QueryTranslatorException { + return Stream.of(values()) + .filter(fieldName -> fieldName.value.equals(fieldNameString)) + .findFirst() + .orElseThrow(() -> new QueryTranslatorException("fieldName: '" + fieldNameString + "' is not supported")); + } + + private final String value; + + FieldName(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + enum Operator { + EQUALS("equals"), + EQUALS_IGNORE_CASE("equalsIgnoreCase"), + CONTAINS("contains"), + CONTAINS_IGNORE_CASE("containsIgnoreCase"), + BEFORE_OR_EQUALS("beforeOrEquals"), + AFTER_OR_EQUALS("afterOrEquals"); + + static Operator getOperator(String operator) throws QueryTranslatorException { + return Stream.of(values()) + .filter(operatorString -> operatorString.value.equals(operator)) + .findFirst() + .orElseThrow(() -> new QueryTranslatorException("operator: '" + operator + "' is not supported")); + } + + private final String value; + + Operator(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + interface FieldValueParser<T> { + + class MailboxIdValueParser implements FieldValueParser<MailboxId> { + + final MailboxId.Factory mailboxIdFactory; + + MailboxIdValueParser(MailboxId.Factory mailboxIdFactory) { + this.mailboxIdFactory = mailboxIdFactory; + } + + @Override + public MailboxId parse(String mailboxIdString) { + return mailboxIdFactory.fromString(mailboxIdString); + } + } + + FieldValueParser<ZonedDateTime> ZONED_DATE_TIME_PARSER = ZonedDateTime::parse; + FieldValueParser<String> STRING_PARSER = input -> input; + FieldValueParser<Boolean> BOOLEAN_PARSER = Boolean::valueOf; + FieldValueParser<MailAddress> MAIL_ADDRESS_PARSER = FieldValueParser::parseMailAddress; + + static MailAddress parseMailAddress(String mailAddressString) throws QueryTranslatorException { + try { + return new MailAddress(mailAddressString); + } catch (AddressException e) { + throw new QueryTranslatorException("mailAddress(" + mailAddressString + ") parsing got error: " + e.getMessage()); + } + } + + T parse(String input); + } + + private final ImmutableTable<FieldName, Operator, Function<String, Criterion>> criterionRegistry; + + @Inject + public QueryTranslator(MailboxId.Factory mailboxIdFactory) { + criterionRegistry = withMailboxIdCriterionParser(mailboxIdFactory); + } + + private ImmutableTable<FieldName, Operator, Function<String, Criterion>> withMailboxIdCriterionParser(MailboxId.Factory mailboxIdFactor) { + FieldValueParser.MailboxIdValueParser mailboxIdParser = new FieldValueParser.MailboxIdValueParser(mailboxIdFactor); + + return defaultRegistryBuilder() + .put(ORIGIN_MAILBOXES, CONTAINS, testedValue -> CriterionFactory.containsOriginMailbox(mailboxIdParser.parse(testedValue))) + .build(); + } + + private ImmutableTable.Builder<FieldName, Operator, Function<String, Criterion>> defaultRegistryBuilder() { + return ImmutableTable.<FieldName, Operator, Function<String, Criterion>>builder() + .put(DELETION_DATE, BEFORE_OR_EQUALS, testedValue -> CriterionFactory.deletionDate().beforeOrEquals(ZONED_DATE_TIME_PARSER.parse(testedValue))) + .put(DELETION_DATE, AFTER_OR_EQUALS, testedValue -> CriterionFactory.deletionDate().afterOrEquals(ZONED_DATE_TIME_PARSER.parse(testedValue))) + .put(DELIVERY_DATE, BEFORE_OR_EQUALS, testedValue -> CriterionFactory.deliveryDate().beforeOrEquals(ZONED_DATE_TIME_PARSER.parse(testedValue))) + .put(DELIVERY_DATE, AFTER_OR_EQUALS, testedValue -> CriterionFactory.deliveryDate().afterOrEquals(ZONED_DATE_TIME_PARSER.parse(testedValue))) + .put(RECIPIENTS, CONTAINS, testedValue -> CriterionFactory.containsRecipient(MAIL_ADDRESS_PARSER.parse(testedValue))) + .put(SENDER, EQUALS, testedValue -> CriterionFactory.hasSender(MAIL_ADDRESS_PARSER.parse(testedValue))) + .put(HAS_ATTACHMENT, EQUALS, testedValue -> CriterionFactory.hasAttachment(BOOLEAN_PARSER.parse(testedValue))) + .put(SUBJECT, EQUALS, testedValue -> CriterionFactory.subject().equals(STRING_PARSER.parse(testedValue))) + .put(SUBJECT, EQUALS_IGNORE_CASE, testedValue -> CriterionFactory.subject().equalsIgnoreCase(STRING_PARSER.parse(testedValue))) + .put(SUBJECT, CONTAINS, testedValue -> CriterionFactory.subject().contains(STRING_PARSER.parse(testedValue))) + .put(SUBJECT, CONTAINS_IGNORE_CASE, testedValue -> CriterionFactory.subject().containsIgnoreCase(STRING_PARSER.parse(testedValue))); + } + + private Criterion translate(CriterionDTO dto) throws QueryTranslatorException { + return Optional.ofNullable(getCriterionParser(dto)) + .map(criterionGeneratingFunction -> criterionGeneratingFunction.apply(dto.getValue())) + .orElseThrow(() -> new QueryTranslatorException("pair of fieldName: '" + dto.getFieldName() + "' and operator: '" + dto.getOperator() + "' is not supported")); + } + + private Function<String, Criterion> getCriterionParser(CriterionDTO dto) { + return criterionRegistry.get( + FieldName.getField(dto.getFieldName()), + Operator.getOperator(dto.getOperator())); + } + + public Query translate(QueryDTO queryDTO) throws QueryTranslatorException { + Preconditions.checkArgument(isAndCombinator(queryDTO.getCombinator()), "combinator '" + queryDTO.getCombinator() + "' is not yet handled"); + Preconditions.checkArgument(queryDTO.getCriteria().stream().allMatch(this::isCriterion), "nested query structure is not yet handled"); + + return Query.of(queryDTO.getCriteria().stream() + .map(queryElement -> (CriterionDTO) queryElement) + .map(Throwing.function(this::translate)) + .collect(Guavate.toImmutableList())); + } + + private boolean isAndCombinator(String combinator) { + return Combinator.AND.getValue().equals(combinator); + } + + private boolean isCriterion(QueryElement queryElement) { + return queryElement instanceof CriterionDTO; + } +} diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializerTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializerTest.java new file mode 100644 index 0000000..d338c9b --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementDeserializerTest.java @@ -0,0 +1,89 @@ +/**************************************************************** + * 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.vault.routes.query; + +import static org.apache.james.vault.DeletedMessageFixture.SUBJECT; +import static org.apache.mailet.base.MailAddressFixture.SENDER; +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName; +import org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +class QueryElementDeserializerTest { + + private ObjectMapper objectMapper; + + @BeforeEach + void beforeEach() { + objectMapper = new ObjectMapper(); + } + + @Test + void shouldDeserializeNestedStructure() throws Exception { + String queryJson = + "{ " + + " \"combinator\": \"and\", " + + " \"criteria\": [ " + + " { " + + " \"combinator\": \"and\", " + + " \"criteria\": [ " + + " {\"fieldName\": \"subject\", \"operator\": \"contains\", \"value\": \"" + SUBJECT + "\"}," + + " {\"fieldName\": \"sender\", \"operator\": \"equals\", \"value\": \"" + SENDER.asString() + "\"}" + + " ] " + + " }, " + + " {\"fieldName\": \"hasAttachment\", \"operator\": \"equals\", \"value\": \"true\"}" + + " ] " + + "} "; + + QueryDTO queryDTO = objectMapper.readValue(queryJson, QueryDTO.class); + assertThat(queryDTO) + .isEqualTo(QueryDTO.and( + QueryDTO.and( + CriterionDTO.from(FieldName.SUBJECT, Operator.CONTAINS, SUBJECT), + CriterionDTO.from(FieldName.SENDER, Operator.EQUALS, SENDER.asString())), + CriterionDTO.from(FieldName.HAS_ATTACHMENT, Operator.EQUALS, "true") + )); + } + + @Test + void shouldDeserializeFlattenStructure() throws Exception { + String queryJson = + "{ " + + " \"combinator\": \"and\", " + + " \"criteria\": [ " + + " {\"fieldName\": \"subject\", \"operator\": \"contains\", \"value\": \"" + SUBJECT + "\"}," + + " {\"fieldName\": \"sender\", \"operator\": \"equals\", \"value\": \"" + SENDER.asString() + "\"}," + + " {\"fieldName\": \"hasAttachment\", \"operator\": \"equals\", \"value\": \"true\"}" + + " ] " + + "} "; + + QueryDTO queryDTO = objectMapper.readValue(queryJson, QueryDTO.class); + assertThat(queryDTO) + .isEqualTo(QueryDTO.and( + CriterionDTO.from(FieldName.SUBJECT, Operator.CONTAINS, SUBJECT), + CriterionDTO.from(FieldName.SENDER, Operator.EQUALS, SENDER.asString()), + CriterionDTO.from(FieldName.HAS_ATTACHMENT, Operator.EQUALS, "true") + )); + } +} \ No newline at end of file diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementTest.java similarity index 60% copy from mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java copy to server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementTest.java index 039fd52..6478a4e 100644 --- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/search/Query.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryElementTest.java @@ -17,33 +17,23 @@ * under the License. * ****************************************************************/ -package org.apache.james.vault.search; +package org.apache.james.webadmin.vault.routes.query; -import java.util.List; -import java.util.function.Predicate; +import org.junit.jupiter.api.Test; -import org.apache.james.vault.DeletedMessage; +import nl.jqno.equalsverifier.EqualsVerifier; -import com.google.common.collect.ImmutableList; +class QueryElementTest { -public class Query { - public static final Query ALL = new Query(ImmutableList.of()); - private static final Predicate<DeletedMessage> MATCH_ALL = any -> true; - - public static Query of(Criterion... criteria) { - return new Query(ImmutableList.copyOf(criteria)); - } - - private final List<Criterion> criteria; - - private Query(List<Criterion> criteria) { - this.criteria = criteria; + @Test + void queryDTOShouldMatchBeanContract() { + EqualsVerifier.forClass(QueryDTO.class) + .verify(); } - public Predicate<DeletedMessage> toPredicate() { - return criteria.stream() - .map(Criterion::toPredicate) - .reduce(Predicate::and) - .orElse(MATCH_ALL); + @Test + void criterionDTOShouldMatchBeanContract() { + EqualsVerifier.forClass(CriterionDTO.class) + .verify(); } -} +} \ No newline at end of file diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryTranslatorTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryTranslatorTest.java new file mode 100644 index 0000000..6689781 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/query/QueryTranslatorTest.java @@ -0,0 +1,67 @@ +/**************************************************************** + * 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.vault.routes.query; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.apache.james.mailbox.inmemory.InMemoryId; +import org.apache.james.webadmin.vault.routes.query.QueryTranslator.FieldName; +import org.apache.james.webadmin.vault.routes.query.QueryTranslator.Operator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableList; + +class QueryTranslatorTest { + + private QueryTranslator queryTranslator; + + @BeforeEach + void beforeEach() { + queryTranslator = new QueryTranslator(new InMemoryId.Factory()); + } + + @Test + void translateShouldThrowWhenPassingNotAndOperator() { + assertThatThrownBy(() -> queryTranslator.translate(new QueryDTO("or", ImmutableList.of()))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("combinator 'or' is not yet handled"); + } + + @Test + void translateShouldThrowWhenPassingNestedQuery() { + assertThatThrownBy(() -> queryTranslator.translate(QueryDTO.and( + QueryDTO.and(new CriterionDTO(FieldName.SUBJECT.getValue(), Operator.CONTAINS.getValue(), "james")) + ))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("nested query structure is not yet handled"); + } + + @Test + void translateShouldNotThrowWhenPassingFlattenQuery() { + assertThatCode(() -> queryTranslator.translate(QueryDTO.and( + new CriterionDTO(FieldName.SUBJECT.getValue(), Operator.CONTAINS.getValue(), "james"), + new CriterionDTO(FieldName.SENDER.getValue(), Operator.EQUALS.getValue(), "u...@james.org"), + new CriterionDTO(FieldName.HAS_ATTACHMENT.getValue(), Operator.EQUALS.getValue(), "true") + ))) + .doesNotThrowAnyException(); + } +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org