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 2018/08/23 12:50:05 UTC

[4/8] james-project git commit: JAMES-2528 implementation of a FilteringManagement API using Event Sourcing

JAMES-2528 implementation of a FilteringManagement API using Event Sourcing


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/58f0f800
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/58f0f800
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/58f0f800

Branch: refs/heads/master
Commit: 58f0f800632189fda5fd1a4b9d5cfd89e991f2c4
Parents: d125ba9
Author: Matthieu Baechler <ma...@apache.org>
Authored: Wed Aug 22 16:08:23 2018 +0200
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Thu Aug 23 14:29:33 2018 +0200

----------------------------------------------------------------------
 server/data/data-jmap/pom.xml                   | 23 ++++-
 .../jmap/api/filtering/FilteringManagement.java | 32 +++++++
 .../apache/james/jmap/api/filtering/Rule.java   | 67 +++++++++++++
 .../api/filtering/impl/DefineRulesCommand.java  | 76 +++++++++++++++
 .../impl/DefineRulesCommandHandler.java         | 52 +++++++++++
 .../impl/EventSourcingFilteringManagement.java  | 69 ++++++++++++++
 .../api/filtering/impl/FilteringAggregate.java  | 90 ++++++++++++++++++
 .../filtering/impl/FilteringAggregateId.java    | 73 +++++++++++++++
 .../jmap/api/filtering/impl/RuleSetDefined.java | 85 +++++++++++++++++
 .../api/filtering/FilteringAggregateIdTest.java | 86 +++++++++++++++++
 .../filtering/FilteringManagementContract.java  | 98 ++++++++++++++++++++
 .../james/jmap/api/filtering/RuleTest.java      | 41 ++++++++
 .../filtering/impl/DefineRulesCommandTest.java  | 51 ++++++++++
 .../EventSourcingFilteringManagementTest.java   | 32 +++++++
 14 files changed, 873 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/pom.xml
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/pom.xml b/server/data/data-jmap/pom.xml
index 7c7f049..169120b 100644
--- a/server/data/data-jmap/pom.xml
+++ b/server/data/data-jmap/pom.xml
@@ -42,6 +42,15 @@
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>event-sourcing-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
             <scope>test</scope>
@@ -55,8 +64,8 @@
             <artifactId>javax.inject</artifactId>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -74,6 +83,16 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/FilteringManagement.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/FilteringManagement.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/FilteringManagement.java
new file mode 100644
index 0000000..a8b1c4b
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/FilteringManagement.java
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.api.filtering;
+
+import java.util.List;
+
+import org.apache.james.core.User;
+
+public interface FilteringManagement {
+
+    void defineRulesForUser(User user, List<Rule> rules);
+
+    List<Rule> listRulesForUser(User user);
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java
new file mode 100644
index 0000000..8bca30a
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.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.jmap.api.filtering;
+
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class Rule {
+
+    private final String id;
+
+    public static Rule of(String id) {
+        return new Rule(id);
+    }
+
+    public Rule(String id) {
+        Preconditions.checkNotNull(id);
+
+        this.id = id;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Rule) {
+            Rule rule = (Rule) o;
+
+            return Objects.equals(this.id, rule.id);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(id);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("id", id)
+            .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommand.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommand.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommand.java
new file mode 100644
index 0000000..0cd256c
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommand.java
@@ -0,0 +1,76 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.james.core.User;
+import org.apache.james.eventsourcing.Command;
+import org.apache.james.jmap.api.filtering.Rule;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class DefineRulesCommand implements Command {
+
+    private final User user;
+    private final List<Rule> rules;
+
+    public DefineRulesCommand(User user, List<Rule> rules) {
+        Preconditions.checkNotNull(user);
+        Preconditions.checkNotNull(rules);
+
+        this.user = user;
+        this.rules = rules;
+    }
+
+    public List<Rule> getRules() {
+        return rules;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof DefineRulesCommand) {
+            DefineRulesCommand that = (DefineRulesCommand) o;
+
+            return Objects.equals(this.user, that.user)
+                && Objects.equals(this.rules, that.rules);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(user, rules);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("user", user)
+            .add("rules", rules)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandHandler.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandHandler.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandHandler.java
new file mode 100644
index 0000000..a613893
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandHandler.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.List;
+
+import org.apache.james.eventsourcing.CommandHandler;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+
+public class DefineRulesCommandHandler implements CommandHandler<DefineRulesCommand> {
+
+    private final EventStore eventStore;
+
+    public DefineRulesCommandHandler(EventStore eventStore) {
+        this.eventStore = eventStore;
+    }
+
+    @Override
+    public Class<DefineRulesCommand> handledClass() {
+        return DefineRulesCommand.class;
+    }
+
+    @Override
+    public List<? extends Event> handle(DefineRulesCommand storeCommand) {
+        FilteringAggregateId aggregateId = new FilteringAggregateId(storeCommand.getUser());
+
+        return FilteringAggregate
+            .load(
+                aggregateId,
+                eventStore.getEventsOfAggregate(aggregateId))
+            .defineRules(storeCommand.getRules());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
new file mode 100644
index 0000000..2fbba65
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.User;
+import org.apache.james.eventsourcing.EventSourcingSystem;
+import org.apache.james.eventsourcing.Subscriber;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.Rule;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+
+public class EventSourcingFilteringManagement implements FilteringManagement {
+
+    private static final ImmutableSet<Subscriber> NO_SUBSCRIBER = ImmutableSet.of();
+
+    private final EventStore eventStore;
+    private final EventSourcingSystem eventSourcingSystem;
+
+    @Inject
+    public EventSourcingFilteringManagement(EventStore eventStore) {
+        this.eventSourcingSystem = new EventSourcingSystem(
+            ImmutableSet.of(new DefineRulesCommandHandler(eventStore)),
+            NO_SUBSCRIBER,
+            eventStore);
+        this.eventStore = eventStore;
+    }
+
+    @Override
+    public void defineRulesForUser(User user, List<Rule> rules) {
+        eventSourcingSystem.dispatch(new DefineRulesCommand(user, rules));
+    }
+
+    @Override
+    public List<Rule> listRulesForUser(User user) {
+        Preconditions.checkNotNull(user);
+
+        FilteringAggregateId aggregateId = new FilteringAggregateId(user);
+
+        return FilteringAggregate
+            .load(
+                aggregateId,
+                eventStore.getEventsOfAggregate(aggregateId))
+            .listRules();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregate.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregate.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregate.java
new file mode 100644
index 0000000..33970f2
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregate.java
@@ -0,0 +1,90 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.List;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.History;
+import org.apache.james.jmap.api.filtering.Rule;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+public class FilteringAggregate {
+
+    public static FilteringAggregate load(FilteringAggregateId aggregateId, History eventsOfAggregate) {
+        return new FilteringAggregate(aggregateId, eventsOfAggregate);
+    }
+
+    private static class State {
+
+        static State initial() {
+            return new State(ImmutableList.of());
+        }
+
+        final ImmutableList<Rule> rules;
+
+        private State(ImmutableList<Rule> rules) {
+            this.rules = rules;
+        }
+
+        State set(ImmutableList<Rule> rules) {
+            return new State(rules);
+        }
+    }
+
+    private final FilteringAggregateId aggregateId;
+    private final History history;
+    private State state;
+
+    private FilteringAggregate(FilteringAggregateId aggregateId, History history) {
+        this.aggregateId = aggregateId;
+        this.state = State.initial();
+        history.getEvents().forEach(this::apply);
+        this.history = history;
+    }
+
+    public List<? extends Event> defineRules(List<Rule> rules) {
+        Preconditions.checkArgument(shouldNotContainDuplicates(rules));
+        ImmutableList<RuleSetDefined> events = ImmutableList.of(
+            new RuleSetDefined(aggregateId, history.getNextEventId(), ImmutableList.copyOf(rules)));
+        events.forEach(this::apply);
+        return events;
+    }
+
+    private boolean shouldNotContainDuplicates(List<Rule> rules) {
+        long uniqueIdCount = rules.stream()
+            .map(Rule::getId)
+            .distinct()
+            .count();
+        return uniqueIdCount == rules.size();
+    }
+
+    public List<Rule> listRules() {
+        return state.rules;
+    }
+
+    private void apply(Event event) {
+        if (event instanceof RuleSetDefined) {
+            state = state.set(((RuleSetDefined)event).getRules());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregateId.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregateId.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregateId.java
new file mode 100644
index 0000000..635f045
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilteringAggregateId.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.Objects;
+
+import org.apache.james.core.User;
+import org.apache.james.eventsourcing.AggregateId;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class FilteringAggregateId implements AggregateId {
+    private static final String SEPARATOR = "/";
+    private static final String PREFIX = "FilteringRule";
+
+    public static final FilteringAggregateId parse(String rawString) {
+        Preconditions.checkArgument(rawString.startsWith(PREFIX + SEPARATOR));
+        return new FilteringAggregateId(User.fromUsername(rawString.substring(PREFIX.length() + SEPARATOR.length())));
+    }
+
+    private final User user;
+
+    public FilteringAggregateId(User user) {
+        Preconditions.checkNotNull(user);
+
+        this.user = user;
+    }
+
+    @Override
+    public String asAggregateKey() {
+        return PREFIX + SEPARATOR + user.asString();
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof FilteringAggregateId) {
+            FilteringAggregateId that = (FilteringAggregateId) o;
+
+            return Objects.equals(this.user, that.user);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(user);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("user", user)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/RuleSetDefined.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/RuleSetDefined.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/RuleSetDefined.java
new file mode 100644
index 0000000..dacba35
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/RuleSetDefined.java
@@ -0,0 +1,85 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import java.util.Objects;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.jmap.api.filtering.Rule;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+public class RuleSetDefined implements Event {
+
+    private final FilteringAggregateId aggregateId;
+    private final EventId eventId;
+    private final ImmutableList<Rule> rules;
+
+    RuleSetDefined(FilteringAggregateId aggregateId, EventId eventId, ImmutableList<Rule> rules) {
+        this.aggregateId = aggregateId;
+        this.eventId = eventId;
+        this.rules = rules;
+    }
+
+    @Override
+    public EventId eventId() {
+        return eventId;
+    }
+
+    @Override
+    public AggregateId getAggregateId() {
+        return aggregateId;
+    }
+
+    public ImmutableList<Rule> getRules() {
+        return rules;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        RuleSetDefined that = (RuleSetDefined) o;
+        return Objects.equals(aggregateId, that.aggregateId) &&
+            Objects.equals(eventId, that.eventId) &&
+            Objects.equals(rules, that.rules);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(aggregateId, eventId, rules);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("aggregateId", aggregateId)
+            .add("eventId", eventId)
+            .add("rules", rules)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringAggregateIdTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringAggregateIdTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringAggregateIdTest.java
new file mode 100644
index 0000000..8588938
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringAggregateIdTest.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * 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.api.filtering;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.core.User;
+
+import org.apache.james.jmap.api.filtering.impl.FilteringAggregateId;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class FilteringAggregateIdTest {
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(FilteringAggregateId.class).verify();
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullDomain() {
+        assertThatThrownBy(() -> new FilteringAggregateId(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void asAggregateKeyShouldReturnAStringContainingThePrefixAndTheDomain() {
+        assertThat(new FilteringAggregateId(User.fromUsername("foo@bar.space")).asAggregateKey())
+            .isEqualTo("FilteringRule/foo@bar.space");
+    }
+
+    @Test
+    void parseShouldThrowWhenNullString() {
+        assertThatThrownBy(() -> FilteringAggregateId.parse(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void parseShouldThrowWhenStringDoesntMatchPrefix() {
+        assertThatThrownBy(() -> FilteringAggregateId.parse("WrongPrefix/foo"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void parseShouldThrowWhenStringDoesntContainSeparator() {
+        assertThatThrownBy(() -> FilteringAggregateId.parse("WrongPrefix"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void parseShouldThrowWhenStringDoesntContainUser() {
+        assertThatThrownBy(() -> FilteringAggregateId.parse("FilteringRule/"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void parseShouldThrowWhenStringDoesntHavePrefix() {
+        assertThatThrownBy(() -> FilteringAggregateId.parse("FilteringRulefoo"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void parseShouldKeepSlashInUsername() {
+        assertThat(FilteringAggregateId.parse("FilteringRule/f/oo@bar.space").asAggregateKey())
+            .isEqualTo("FilteringRule/f/oo@bar.space");
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringManagementContract.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringManagementContract.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringManagementContract.java
new file mode 100644
index 0000000..98683b8
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/FilteringManagementContract.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.api.filtering;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.core.User;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public interface FilteringManagementContract {
+
+    String BART_SIMPSON_CARTOON = "bart@simpson.cartoon";
+    Rule RULE_1 = Rule.of("1");
+    Rule RULE_2 = Rule.of("2");
+    Rule RULE_3 = Rule.of("3");
+
+    FilteringManagement instanciateFilteringManagement();
+
+    @Test
+    default void listingRulesForUnknownUserShouldReturnEmptyList() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        assertThat(instanciateFilteringManagement().listRulesForUser(user)).isEmpty();
+    }
+
+    @Test
+    default void listingRulesShouldThrowWhenNullUser() {
+        User user = null;
+        assertThatThrownBy(() -> instanciateFilteringManagement().listRulesForUser(user)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    default void listingRulesShouldReturnDefinedRules() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        FilteringManagement testee = instanciateFilteringManagement();
+        testee.defineRulesForUser(user, ImmutableList.of(RULE_1, RULE_2));
+        assertThat(testee.listRulesForUser(user)).containsExactly(RULE_1, RULE_2);
+    }
+
+    @Test
+    default void listingRulesShouldReturnLastDefinedRules() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        FilteringManagement testee = instanciateFilteringManagement();
+        testee.defineRulesForUser(user, ImmutableList.of(RULE_1, RULE_2));
+        testee.defineRulesForUser(user, ImmutableList.of(RULE_2, RULE_1));
+        assertThat(testee.listRulesForUser(user)).containsExactly(RULE_2, RULE_1);
+    }
+
+    @Test
+    default void definingRulesShouldThrowWhenDuplicateRules() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        FilteringManagement testee = instanciateFilteringManagement();
+        assertThatThrownBy(() -> testee.defineRulesForUser(user, ImmutableList.of(RULE_1, RULE_1)))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    default void definingRulesShouldThrowWhenNullUser() {
+        FilteringManagement testee = instanciateFilteringManagement();
+        assertThatThrownBy(() -> testee.defineRulesForUser(null, ImmutableList.of(RULE_1, RULE_1)))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    default void definingRulesShouldThrowWhenNullRuleList() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        FilteringManagement testee = instanciateFilteringManagement();
+        assertThatThrownBy(() -> testee.defineRulesForUser(user, null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    default void definingRulesShouldKeepOrdering() {
+        User user = User.fromUsername(BART_SIMPSON_CARTOON);
+        FilteringManagement testee = instanciateFilteringManagement();
+        testee.defineRulesForUser(user, ImmutableList.of(RULE_3, RULE_2, RULE_1));
+        assertThat(testee.listRulesForUser(user)).containsExactly(RULE_3, RULE_2, RULE_1);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleTest.java
new file mode 100644
index 0000000..fc10f6a
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleTest.java
@@ -0,0 +1,41 @@
+/****************************************************************
+ * 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.api.filtering;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class RuleTest {
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(Rule.class)
+            .verify();
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullId() {
+        assertThatThrownBy(() -> new Rule(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandTest.java
new file mode 100644
index 0000000..a917fdd
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/DefineRulesCommandTest.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.jmap.api.filtering.impl;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.core.User;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class DefineRulesCommandTest {
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(DefineRulesCommand.class)
+            .verify();
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullUser() {
+        assertThatThrownBy(() -> new DefineRulesCommand(null, ImmutableList.of(Rule.of("1"), Rule.of("2"))))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullRuleList() {
+        assertThatThrownBy(() -> new DefineRulesCommand(User.fromUsername("adam@james.org"), null))
+            .isInstanceOf(NullPointerException.class);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/58f0f800/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagementTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagementTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagementTest.java
new file mode 100644
index 0000000..45120c3
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagementTest.java
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.api.filtering.impl;
+
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStore;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.FilteringManagementContract;
+
+public class EventSourcingFilteringManagementTest implements FilteringManagementContract {
+
+    @Override
+    public FilteringManagement instanciateFilteringManagement() {
+        return new EventSourcingFilteringManagement(new InMemoryEventStore());
+    }
+}


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