james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject [08/15] james-project git commit: MAILBOX-332 Cassandra implementation for Quota Threshold Crossing notification
Date Thu, 10 May 2018 02:31:15 GMT
MAILBOX-332 Cassandra implementation for Quota Threshold Crossing notification


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

Branch: refs/heads/master
Commit: c3a1bda2d0bd84c936b5848d06ec6fd9a176868c
Parents: a686c2b
Author: Matthieu Baechler <matthieu@apache.org>
Authored: Mon May 7 18:05:45 2018 +0200
Committer: benwa <btellier@linagora.com>
Committed: Thu May 10 09:15:40 2018 +0700

----------------------------------------------------------------------
 .../cassandra/DockerCassandraExtension.java     |   4 +
 .../apache/james/mailbox/quota/QuotaCount.java  |   6 +-
 .../apache/james/mailbox/quota/QuotaSize.java   |   6 +-
 mailbox/plugin/quota-mailing-cassandra/pom.xml  | 135 +++++++++++++++++
 .../cassandra/CassandraEventStore.java          |  62 ++++++++
 .../cassandra/CassandraEventStoreModule.java    |  62 ++++++++
 .../cassandra/CassandraEventStoreTable.java     |  27 ++++
 .../eventsourcing/cassandra/EventStoreDao.java  | 122 +++++++++++++++
 .../cassandra/JsonEventSerializer.java          |  98 +++++++++++++
 .../eventsourcing/cassandra/dto/EventDTO.java   |  26 ++++
 .../cassandra/dto/EventDTOModule.java           |  32 ++++
 .../cassandra/dto/HistoryEvolutionDTO.java      |  98 +++++++++++++
 .../mailbox/quota/cassandra/dto/QuotaDTO.java   |  73 +++++++++
 .../dto/QuotaThresholdChangedEventDTO.java      | 110 ++++++++++++++
 .../QuotaThresholdChangedEventDTOModule.java    |  54 +++++++
 .../CassandraEventSourcingSystemTest.java       |  28 ++++
 .../cassandra/CassandraEventStoreExtension.java |  86 +++++++++++
 .../cassandra/CassandraEventStoreTest.java      |  28 ++++
 .../cassandra/JsonEventSerializerTest.java      |  67 +++++++++
 .../cassandra/dto/TestEventDTO.java             |  73 +++++++++
 .../cassandra/dto/TestEventDTOModule.java       |  56 +++++++
 .../mailbox/quota/cassandra/dto/DTOTest.java    | 147 +++++++++++++++++++
 ...draQuotaMailingListenersIntegrationTest.java |  29 ++++
 .../james/eventsource/InMemoryEventStore.java   |  11 +-
 .../org/apache/james/eventsourcing/Event.java   |  11 ++
 .../events/QuotaThresholdChangedEvent.java      |  20 +++
 .../mailbox/quota/model/HistoryEvolution.java   |  22 +--
 .../quota/model/QuotaThresholdHistory.java      |   8 +
 .../eventsourcing/EventSourcingSystemTest.java  |   3 +-
 .../james/eventsourcing/EventStoreTest.java     |   2 +-
 .../james/eventsourcing/TestAggregateId.java    |   4 +
 mailbox/pom.xml                                 |   1 +
 32 files changed, 1488 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
index 43a3cb5..add70c8 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
@@ -56,6 +56,10 @@ public class DockerCassandraExtension implements BeforeAllCallback, AfterAllCall
     public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
         return dockerCassandra;
     }
+
+    public DockerCassandra getDockerCassandra() {
+        return dockerCassandra;
+    }
     
     public static class DockerCassandra {
         

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaCount.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaCount.java b/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaCount.java
index a932c33..ac17fa0 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaCount.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaCount.java
@@ -30,7 +30,11 @@ public class QuotaCount implements QuotaValue<QuotaCount> {
     }
 
     public static QuotaCount count(long value) {
-        return new QuotaCount(Optional.of(value));
+        return count(Optional.of(value));
+    }
+
+    public static QuotaCount count(Optional<Long> value) {
+        return new QuotaCount(value);
     }
 
     private final Optional<Long> value;

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaSize.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaSize.java b/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaSize.java
index c8fb1a0..4d16e52 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaSize.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/quota/QuotaSize.java
@@ -32,7 +32,11 @@ public class QuotaSize implements QuotaValue<QuotaSize> {
     }
 
     public static QuotaSize size(long value) {
-        return new QuotaSize(Optional.of(value));
+        return size(Optional.of(value));
+    }
+
+    public static QuotaSize size(Optional<Long> value) {
+        return new QuotaSize(value);
     }
 
     private final Optional<Long> value;

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/pom.xml b/mailbox/plugin/quota-mailing-cassandra/pom.xml
new file mode 100644
index 0000000..38dbda2
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/pom.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>apache-james-mailbox</artifactId>
+        <groupId>org.apache.james</groupId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>quota-mailing-cassandra</artifactId>
+    <name>Apache James :: Mailbox :: Plugin :: Quota Mailing :: Cassandra</name>
+    <description>Apache James Mailbox Cassandra implementation of Quota mailing listener</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-backends-cassandra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-backends-cassandra</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-quota-mailing</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-quota-mailing</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-store</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-mailet-base</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-data-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit</artifactId>
+            <version>1.5.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-fluent</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
new file mode 100644
index 0000000..61c5a06
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventStore;
+
+import com.google.common.base.Preconditions;
+
+public class CassandraEventStore implements EventStore {
+
+    private final EventStoreDao eventStoreDao;
+
+    @Inject
+    public CassandraEventStore(EventStoreDao eventStoreDao) {
+        this.eventStoreDao = eventStoreDao;
+    }
+
+    @Override
+    public void appendAll(List<Event> events) {
+        if (events.isEmpty()) {
+            return;
+        }
+        doAppendAll(events);
+    }
+
+    public void doAppendAll(List<Event> events) {
+        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
+
+        boolean success = eventStoreDao.appendAll(events).join();
+        if (!success) {
+            throw new EventStoreFailedException();
+        }
+    }
+
+    @Override
+    public History getEventsOfAggregate(AggregateId aggregateId) {
+        return eventStoreDao.getEventsOfAggregate(aggregateId);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
new file mode 100644
index 0000000..ed15cb2
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+import java.util.List;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.components.CassandraTable;
+import org.apache.james.backends.cassandra.components.CassandraType;
+import org.apache.james.backends.cassandra.utils.CassandraConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.google.common.collect.ImmutableList;
+
+public class CassandraEventStoreModule implements CassandraModule {
+    private final List<CassandraTable> tables;
+    private final List<CassandraType> types;
+
+    public CassandraEventStoreModule() {
+        tables = ImmutableList.of(
+            new CassandraTable(CassandraEventStoreTable.EVENTS_TABLE,
+                SchemaBuilder.createTable(CassandraEventStoreTable.EVENTS_TABLE)
+                    .ifNotExists()
+                    .addPartitionKey(CassandraEventStoreTable.AGGREGATE_ID, DataType.varchar())
+                    .addClusteringColumn(CassandraEventStoreTable.EVENT_ID, DataType.cint())
+                    .addColumn(CassandraEventStoreTable.EVENT, DataType.text())
+                    .withOptions()
+                    .comment("Store events of a EventSourcing aggregate")
+                    .caching(SchemaBuilder.KeyCaching.ALL,
+                            SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION))));
+        types = ImmutableList.of();
+    }
+
+    @Override
+    public List<CassandraTable> moduleTables() {
+        return tables;
+    }
+
+    @Override
+    public List<CassandraType> moduleTypes() {
+        return types;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
new file mode 100644
index 0000000..c90b81e
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+public interface CassandraEventStoreTable {
+    String EVENTS_TABLE = "eventStore";
+    String AGGREGATE_ID = "aggregateId";
+    String EVENT = "event";
+    String EVENT_ID = "eventId";
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
new file mode 100644
index 0000000..2518f02
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java
@@ -0,0 +1,122 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.AGGREGATE_ID;
+import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENT;
+import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENTS_TABLE;
+import static org.apache.james.eventsourcing.cassandra.CassandraEventStoreTable.EVENT_ID;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventStore;
+
+import com.datastax.driver.core.BatchStatement;
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.github.steveash.guavate.Guavate;
+
+public class EventStoreDao {
+    private final CassandraUtils cassandraUtils;
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement insert;
+    private final PreparedStatement select;
+    private final JsonEventSerializer jsonEventSerializer;
+
+    @Inject
+    public EventStoreDao(Session session, CassandraUtils cassandraUtils, JsonEventSerializer jsonEventSerializer) {
+        this.cassandraUtils = cassandraUtils;
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        this.jsonEventSerializer = jsonEventSerializer;
+        this.insert = prepareInsert(session);
+        this.select = prepareSelect(session);
+    }
+
+    private PreparedStatement prepareInsert(Session session) {
+        return session.prepare(insertInto(EVENTS_TABLE)
+            .value(AGGREGATE_ID, bindMarker(AGGREGATE_ID))
+            .value(EVENT_ID, bindMarker(EVENT_ID))
+            .value(EVENT, bindMarker(EVENT))
+            .ifNotExists());
+    }
+
+    private PreparedStatement prepareSelect(Session session) {
+        return session.prepare(select()
+            .from(EVENTS_TABLE)
+            .where(eq(AGGREGATE_ID, bindMarker(AGGREGATE_ID))));
+    }
+
+    public CompletableFuture<Boolean> appendAll(List<Event> events) {
+        BatchStatement batch = new BatchStatement();
+        events.forEach(event -> batch.add(insertEvent(event)));
+        return cassandraAsyncExecutor.executeReturnApplied(batch);
+    }
+
+    private BoundStatement insertEvent(Event event) {
+        try {
+            return insert
+                .bind()
+                .setString(AGGREGATE_ID, event.getAggregateId().asAggregateKey())
+                .setInt(EVENT_ID, event.eventId().serialize())
+                .setString(EVENT, jsonEventSerializer.serialize(event));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public EventStore.History getEventsOfAggregate(AggregateId aggregateId) {
+        return toHistory(
+            cassandraAsyncExecutor.execute(
+                select.bind()
+                    .setString(AGGREGATE_ID, aggregateId.asAggregateKey()))
+                .join());
+    }
+
+    private EventStore.History toHistory(ResultSet resultSet) {
+        List<Event> events = cassandraUtils.convertToStream(resultSet)
+            .map(this::toEvent)
+            .collect(Guavate.toImmutableList());
+        return EventStore.History.of(events);
+    }
+
+    private Event toEvent(Row row) {
+        try {
+            return jsonEventSerializer.deserialize(row.getString(EVENT));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java
new file mode 100644
index 0000000..bf3a5dc
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.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.eventsourcing.cassandra;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
+import org.apache.james.eventsourcing.cassandra.dto.EventDTOModule;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableSet;
+
+public class JsonEventSerializer {
+    public static class UnknownEventException extends RuntimeException {
+        public UnknownEventException(String message) {
+            super(message);
+        }
+    }
+
+    private final Map<Class<? extends Event>, EventDTOModule> eventClassToModule;
+    private final Map<String, EventDTOModule> typeToModule;
+    private final ObjectMapper objectMapper;
+
+    @Inject
+    public JsonEventSerializer(Set<EventDTOModule> modules) {
+        objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new Jdk8Module());
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
+
+        typeToModule = modules.stream()
+            .collect(Guavate.toImmutableMap(
+                EventDTOModule::getType,
+                Function.identity()));
+
+        eventClassToModule = modules.stream()
+            .collect(Guavate.toImmutableMap(
+                EventDTOModule::getEventClass,
+                Function.identity()));
+    }
+    
+    public JsonEventSerializer(EventDTOModule... modules) {
+        this(ImmutableSet.copyOf(modules));
+    }
+
+    public String serialize(Event event) throws JsonProcessingException {
+        Object dto = Optional.ofNullable(eventClassToModule.get(event.getClass()))
+            .orElseThrow(() -> new UnknownEventException("unknown event class " + event.getClass()))
+            .toDTO(event);
+        return objectMapper.writeValueAsString(dto);
+    }
+
+    public Event deserialize(String value) throws IOException {
+        JsonNode jsonNode = objectMapper.readTree(value);
+
+        String type = jsonNode.path("type").asText();
+
+        EventDTO dto = objectMapper.readValue(
+            objectMapper.treeAsTokens(jsonNode),
+            retrieveDTOClass(type));
+        return dto.toEvent();
+    }
+
+    public Class<? extends EventDTO> retrieveDTOClass(String type) {
+        return Optional.ofNullable(typeToModule.get(type))
+            .map(EventDTOModule::getDTOClass)
+            .orElseThrow(() -> new UnknownEventException("unknown event type " + type));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
new file mode 100644
index 0000000..4616248
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+
+public interface EventDTO {
+    Event toEvent();
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java
new file mode 100644
index 0000000..d86d1d7
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.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.eventsourcing.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+
+public interface EventDTOModule {
+    String getType();
+
+    Class<? extends EventDTO> getDTOClass();
+
+    Class<? extends Event> getEventClass();
+
+    EventDTO toDTO(Event event);
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/HistoryEvolutionDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/HistoryEvolutionDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/HistoryEvolutionDTO.java
new file mode 100644
index 0000000..2d1dda1
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/HistoryEvolutionDTO.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.mailbox.quota.cassandra.dto;
+
+import java.time.Instant;
+import java.util.Optional;
+
+import org.apache.james.mailbox.quota.model.HistoryEvolution;
+import org.apache.james.mailbox.quota.model.QuotaThreshold;
+import org.apache.james.mailbox.quota.model.QuotaThresholdChange;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.Booleans;
+
+class HistoryEvolutionDTO {
+
+    public static HistoryEvolutionDTO toDto(HistoryEvolution historyEvolution) {
+        return new HistoryEvolutionDTO(
+            historyEvolution.getThresholdHistoryChange(),
+            historyEvolution.getRecentness(),
+            historyEvolution.getThresholdChange()
+                .map(QuotaThresholdChange::getQuotaThreshold)
+                .map(QuotaThreshold::getQuotaOccupationRatio),
+            historyEvolution.getThresholdChange()
+                .map(QuotaThresholdChange::getInstant)
+                .map(Instant::toEpochMilli));
+    }
+
+    private final HistoryEvolution.HistoryChangeType change;
+    private final Optional<HistoryEvolution.HighestThresholdRecentness> recentness;
+    private final Optional<Double> threshold;
+    private final Optional<Long> instant;
+
+    @JsonCreator
+    public HistoryEvolutionDTO(
+            @JsonProperty("changeType") HistoryEvolution.HistoryChangeType change,
+            @JsonProperty("recentness") Optional<HistoryEvolution.HighestThresholdRecentness> recentness,
+            @JsonProperty("threshold") Optional<Double> threshold,
+            @JsonProperty("instant") Optional<Long> instant) {
+        this.change = change;
+        this.recentness = recentness;
+        this.threshold = threshold;
+        this.instant = instant;
+    }
+
+    public HistoryEvolution.HistoryChangeType getChange() {
+        return change;
+    }
+
+    public Optional<HistoryEvolution.HighestThresholdRecentness> getRecentness() {
+        return recentness;
+    }
+
+    public Optional<Double> getThreshold() {
+        return threshold;
+    }
+
+    public Optional<Long> getInstant() {
+        return instant;
+    }
+
+    @JsonIgnore
+    public HistoryEvolution toHistoryEvolution() {
+        Preconditions.checkState(Booleans.countTrue(
+            threshold.isPresent(), instant.isPresent()) != 1,
+            "threshold and instant needs to be both set, or both unset. Mixed states not allowed.");
+
+        Optional<QuotaThresholdChange> quotaThresholdChange = threshold
+            .map(QuotaThreshold::new)
+            .map(value -> new QuotaThresholdChange(value, Instant.ofEpochMilli(instant.get())));
+
+        return new HistoryEvolution(
+            change,
+            recentness,
+            quotaThresholdChange);
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaDTO.java
new file mode 100644
index 0000000..bf204c8
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaDTO.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.mailbox.quota.cassandra.dto;
+
+import java.util.Optional;
+
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.quota.QuotaCount;
+import org.apache.james.mailbox.quota.QuotaSize;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+class QuotaDTO {
+    public static QuotaDTO from(Quota<?> quota) {
+        if (quota.getLimit().isUnlimited()) {
+            return new QuotaDTO(quota.getUsed().asLong(), Optional.empty());
+        }
+        return new QuotaDTO(quota.getUsed().asLong(), Optional.of(quota.getLimit().asLong()));
+    }
+
+    private final long used;
+    private final Optional<Long> limit;
+
+    @JsonCreator
+    private QuotaDTO(@JsonProperty("used") long used,
+                     @JsonProperty("limit") Optional<Long> limit) {
+        this.used = used;
+        this.limit = limit;
+    }
+
+    public long getUsed() {
+        return used;
+    }
+
+    public Optional<Long> getLimit() {
+        return limit;
+    }
+
+    @JsonIgnore
+    public Quota<QuotaSize> asSizeQuota() {
+        return Quota.<QuotaSize>builder()
+            .used(QuotaSize.size(used))
+            .computedLimit(QuotaSize.size(limit))
+            .build();
+    }
+
+    @JsonIgnore
+    public Quota<QuotaCount> asCountQuota() {
+        return Quota.<QuotaCount>builder()
+            .used(QuotaCount.count(used))
+            .computedLimit(QuotaCount.count(limit))
+            .build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
new file mode 100644
index 0000000..80eae8d
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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.mailbox.quota.cassandra.dto;
+
+import org.apache.james.core.User;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
+import org.apache.james.mailbox.quota.mailing.aggregates.UserQuotaThresholds;
+import org.apache.james.mailbox.quota.mailing.events.QuotaThresholdChangedEvent;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+class QuotaThresholdChangedEventDTO implements EventDTO {
+
+    @JsonIgnore
+    public static QuotaThresholdChangedEventDTO from(QuotaThresholdChangedEvent event, String type) {
+        return new QuotaThresholdChangedEventDTO(
+            type, event.eventId().serialize(),
+            event.getAggregateId().getUser().asString(),
+            QuotaDTO.from(event.getSizeQuota()),
+            QuotaDTO.from(event.getCountQuota()),
+            HistoryEvolutionDTO.toDto(event.getSizeHistoryEvolution()),
+            HistoryEvolutionDTO.toDto(event.getCountHistoryEvolution()));
+    }
+
+    private final String type;
+    private final int eventId;
+    private final String user;
+    private final QuotaDTO sizeQuota;
+    private final QuotaDTO countQuota;
+    private final HistoryEvolutionDTO sizeEvolution;
+    private final HistoryEvolutionDTO countEvolution;
+
+    @JsonCreator
+    private QuotaThresholdChangedEventDTO(
+            @JsonProperty("type") String type,
+            @JsonProperty("eventId") int eventId,
+            @JsonProperty("user") String user,
+            @JsonProperty("sizeQuota") QuotaDTO sizeQuota,
+            @JsonProperty("countQuota") QuotaDTO countQuota,
+            @JsonProperty("sizeEvolution") HistoryEvolutionDTO sizeEvolution,
+            @JsonProperty("countEvolution") HistoryEvolutionDTO countEvolution) {
+        this.type = type;
+        this.eventId = eventId;
+        this.user = user;
+        this.sizeQuota = sizeQuota;
+        this.countQuota = countQuota;
+        this.sizeEvolution = sizeEvolution;
+        this.countEvolution = countEvolution;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public long getEventId() {
+        return eventId;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public QuotaDTO getSizeQuota() {
+        return sizeQuota;
+    }
+
+    public QuotaDTO getCountQuota() {
+        return countQuota;
+    }
+
+    public HistoryEvolutionDTO getSizeEvolution() {
+        return sizeEvolution;
+    }
+
+    public HistoryEvolutionDTO getCountEvolution() {
+        return countEvolution;
+    }
+
+    @JsonIgnore
+    @Override
+    public QuotaThresholdChangedEvent toEvent() {
+        return new QuotaThresholdChangedEvent(
+            EventId.fromSerialized(eventId),
+            sizeEvolution.toHistoryEvolution(),
+            countEvolution.toHistoryEvolution(),
+            sizeQuota.asSizeQuota(),
+            countQuota.asCountQuota(),
+            UserQuotaThresholds.Id.from(User.fromUsername(user)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
new file mode 100644
index 0000000..328cb09
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java
@@ -0,0 +1,54 @@
+/****************************************************************
+ * 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.mailbox.quota.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.cassandra.dto.EventDTO;
+import org.apache.james.eventsourcing.cassandra.dto.EventDTOModule;
+import org.apache.james.mailbox.quota.mailing.events.QuotaThresholdChangedEvent;
+
+import com.google.common.base.Preconditions;
+
+public class QuotaThresholdChangedEventDTOModule implements EventDTOModule {
+    private static final String QUOTA_THRESHOLD_CHANGE = "quota-threshold-change";
+
+    @Override
+    public String getType() {
+        return QUOTA_THRESHOLD_CHANGE;
+    }
+
+    @Override
+    public Class<? extends EventDTO> getDTOClass() {
+        return QuotaThresholdChangedEventDTO.class;
+    }
+
+    @Override
+    public Class<? extends Event> getEventClass() {
+        return QuotaThresholdChangedEvent.class;
+    }
+
+    @Override
+    public EventDTO toDTO(Event event) {
+        Preconditions.checkArgument(event instanceof QuotaThresholdChangedEvent);
+        return QuotaThresholdChangedEventDTO.from(
+            (QuotaThresholdChangedEvent) event,
+            QUOTA_THRESHOLD_CHANGE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
new file mode 100644
index 0000000..bee6e88
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+import org.apache.james.eventsourcing.EventSourcingSystemTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(CassandraEventStoreExtension.class)
+public class CassandraEventSourcingSystemTest implements EventSourcingSystemTest {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java
new file mode 100644
index 0000000..1b0066f
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.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.eventsourcing.cassandra;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.DockerCassandraExtension;
+import org.apache.james.backends.cassandra.DockerCassandraExtension.DockerCassandra;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.eventsourcing.EventStore;
+import org.apache.james.eventsourcing.cassandra.dto.TestEventDTOModule;
+import org.apache.james.mailbox.quota.cassandra.dto.QuotaThresholdChangedEventDTOModule;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public class CassandraEventStoreExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
+    private final DockerCassandraExtension dockerCassandraExtension;
+    private CassandraCluster cassandra;
+    private DockerCassandra dockerCassandra;
+    private EventStoreDao eventStoreDao;
+
+    public CassandraEventStoreExtension() {
+        dockerCassandraExtension = new DockerCassandraExtension();
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        dockerCassandraExtension.beforeAll(context);
+        dockerCassandra = dockerCassandraExtension.getDockerCassandra();
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        dockerCassandraExtension.afterAll(context);
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) {
+        cassandra = CassandraCluster.create(
+                new CassandraEventStoreModule(), dockerCassandra.getIp(), dockerCassandra.getBindingPort());
+
+        JsonEventSerializer jsonEventSerializer = new JsonEventSerializer(
+            new QuotaThresholdChangedEventDTOModule(),
+            new TestEventDTOModule());
+
+        eventStoreDao = new EventStoreDao(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION,
+            jsonEventSerializer);
+    }
+
+    @Override
+    public void afterEach(ExtensionContext context) {
+        cassandra.close();
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == EventStore.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return new CassandraEventStore(eventStoreDao);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
new file mode 100644
index 0000000..314b185
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra;
+
+import org.apache.james.eventsourcing.EventStoreTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(CassandraEventStoreExtension.class)
+class CassandraEventStoreTest implements EventStoreTest {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java
new file mode 100644
index 0000000..893647b
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.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.eventsourcing.cassandra;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+import org.apache.james.eventsourcing.TestEvent;
+import org.apache.james.eventsourcing.cassandra.dto.TestEventDTOModule;
+import org.junit.jupiter.api.Test;
+
+class JsonEventSerializerTest {
+    public static final String TEST_EVENT_JSON = "{\"type\":\"TestType\",\"data\":\"first\",\"eventId\":0,\"aggregate\":1}";
+    public static final TestEvent TEST_EVENT = new TestEvent(
+        EventId.fromSerialized(0),
+        TestAggregateId.testId(1),
+        "first");
+
+    @Test
+    void shouldDeserializeKnownEvent() throws Exception {
+        assertThat(new JsonEventSerializer(new TestEventDTOModule())
+            .deserialize(TEST_EVENT_JSON))
+            .isEqualTo(TEST_EVENT);
+    }
+
+    @Test
+    void shouldThrowWhenDeserializeUnknownEvent() {
+        assertThatThrownBy(() -> new JsonEventSerializer()
+            .deserialize(TEST_EVENT_JSON))
+            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
+    }
+
+    @Test
+    void shouldSerializeKnownEvent() throws Exception {
+        assertThatJson(new JsonEventSerializer(new TestEventDTOModule())
+            .serialize(TEST_EVENT))
+            .isEqualTo(TEST_EVENT_JSON);
+    }
+
+    @Test
+    void shouldThrowWhenSerializeUnknownEvent() {
+        assertThatThrownBy(() -> new JsonEventSerializer()
+            .serialize(TEST_EVENT))
+            .isInstanceOf(JsonEventSerializer.UnknownEventException.class);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java
new file mode 100644
index 0000000..529d8a9
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.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.eventsourcing.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.TestAggregateId;
+import org.apache.james.eventsourcing.TestEvent;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TestEventDTO implements EventDTO {
+    private final String type;
+    private final String data;
+    private final int eventId;
+    private final int aggregate;
+
+    @JsonCreator
+    public TestEventDTO(
+            @JsonProperty("type") String type,
+            @JsonProperty("data") String data,
+            @JsonProperty("eventId") int eventId,
+            @JsonProperty("aggregate") int aggregate) {
+        this.type = type;
+        this.data = data;
+        this.eventId = eventId;
+        this.aggregate = aggregate;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public long getEventId() {
+        return eventId;
+    }
+
+    public int getAggregate() {
+        return aggregate;
+    }
+
+    @JsonIgnore
+    @Override
+    public Event toEvent() {
+        return new TestEvent(
+            EventId.fromSerialized(eventId),
+            TestAggregateId.testId(aggregate),
+            data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
new file mode 100644
index 0000000..6b46268
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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.eventsourcing.cassandra.dto;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.TestEvent;
+import org.testcontainers.shaded.com.google.common.base.Preconditions;
+
+public class TestEventDTOModule implements EventDTOModule {
+
+    public static final String TEST_TYPE = "TestType";
+
+    @Override
+    public String getType() {
+        return TEST_TYPE;
+    }
+
+    @Override
+    public Class<? extends EventDTO> getDTOClass() {
+        return TestEventDTO.class;
+    }
+
+    @Override
+    public Class<? extends Event> getEventClass() {
+        return TestEvent.class;
+    }
+
+    @Override
+    public EventDTO toDTO(Event event) {
+        Preconditions.checkArgument(event instanceof TestEvent);
+
+        TestEvent testEvent = (TestEvent) event;
+        return new TestEventDTO(
+            TEST_TYPE,
+            testEvent.getData(),
+            testEvent.eventId().serialize(),
+            testEvent.getAggregateId().getId());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
new file mode 100644
index 0000000..a42725a
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java
@@ -0,0 +1,147 @@
+/****************************************************************
+ * 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.mailbox.quota.cassandra.dto;
+
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.core.User;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.cassandra.JsonEventSerializer;
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.quota.QuotaCount;
+import org.apache.james.mailbox.quota.QuotaSize;
+import org.apache.james.mailbox.quota.mailing.aggregates.UserQuotaThresholds;
+import org.apache.james.mailbox.quota.mailing.events.QuotaThresholdChangedEvent;
+import org.apache.james.mailbox.quota.model.HistoryEvolution;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+
+class DTOTest {
+
+    public static final Quota<QuotaSize> SIZE_QUOTA = Quota.<QuotaSize>builder().used(QuotaSize.size(23)).computedLimit(QuotaSize.size(33)).build();
+    public static final Quota<QuotaCount> COUNT_QUOTA = Quota.<QuotaCount>builder().used(QuotaCount.count(12)).computedLimit(QuotaCount.count(45)).build();
+    public static final QuotaThresholdChangedEvent EVENT = new QuotaThresholdChangedEvent(
+        EventId.first(),
+        HistoryEvolution.noChanges(),
+        HistoryEvolution.noChanges(),
+        SIZE_QUOTA,
+        COUNT_QUOTA,
+        UserQuotaThresholds.Id.from(User.fromUsername("foo@bar.com")));
+
+    public static final String EVENT_JSON = "{" +
+        " \"type\": \"quota-threshold-change\"," +
+        " \"eventId\": 0," +
+        " \"user\": \"foo@bar.com\"," +
+        " \"sizeQuota\": {" +
+        "   \"used\": 23," +
+        "   \"limit\": 33" +
+        " }," +
+        " \"countQuota\": {" +
+        "   \"used\": 12," +
+        "   \"limit\": 45" +
+        " }," +
+        " \"sizeEvolution\": {" +
+        "   \"change\": \"NoChange\"," +
+        "   \"threshold\": null," +
+        "   \"instant\": null," +
+        "   \"recentness\": null" +
+        " }," +
+        " \"countEvolution\": {" +
+        "   \"change\": \"NoChange\"," +
+        "   \"threshold\": null," +
+        "   \"instant\": null," +
+        "   \"recentness\": null" +
+        " }" +
+        "}";
+
+    public static final String COUNT_QUOTA_JSON = "{" +
+        "   \"used\": 12," +
+        "   \"limit\": 45" +
+        " }";
+
+    public static final String NO_CHANGES_JSON = "{" +
+        "  \"change\":\"NoChange\"" +
+        "}";
+
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    public void setUp() {
+        objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new Jdk8Module());
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
+    }
+
+    @Test
+    void shouldSerializeQuotaDTO() throws Exception {
+        assertThatJson(objectMapper.writeValueAsString(QuotaDTO.from(COUNT_QUOTA)))
+            .isEqualTo(COUNT_QUOTA_JSON);
+    }
+
+    @Test
+    void shouldDeserializeQuotaDTO() throws Exception {
+        assertThat(objectMapper.readValue(COUNT_QUOTA_JSON, QuotaDTO.class).asCountQuota())
+            .isEqualTo(COUNT_QUOTA);
+    }
+
+    @Test
+    void shouldSerializeHistoryEvolutionDTO() throws Exception {
+        assertThatJson(objectMapper.writeValueAsString(HistoryEvolutionDTO.toDto(
+            HistoryEvolution.noChanges())))
+            .isEqualTo(NO_CHANGES_JSON);
+    }
+
+    @Test
+    void shouldDeserializeHistoryEvolutionDTO() throws Exception {
+        assertThat(objectMapper.readValue(NO_CHANGES_JSON, HistoryEvolutionDTO.class)
+            .toHistoryEvolution())
+            .isEqualTo(HistoryEvolution.noChanges());
+    }
+
+    @Test
+    void shouldSerializeQuotaThresholdChangedEventDTO() throws Exception {
+        assertThatJson(objectMapper.writeValueAsString(
+            new QuotaThresholdChangedEventDTOModule().toDTO(EVENT)))
+            .isEqualTo(EVENT_JSON);
+    }
+
+    @Test
+    void shouldDeserializeQuotaThresholdChangedEventDTO() throws Exception {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new Jdk8Module());
+
+        assertThat(objectMapper.readValue(EVENT_JSON, QuotaThresholdChangedEventDTO.class)
+            .toEvent())
+            .isEqualTo(EVENT);
+    }
+
+    @Test
+    void shouldSerializeQuotaThresholdChangedEvent() throws Exception {
+        assertThatJson(new JsonEventSerializer(new QuotaThresholdChangedEventDTOModule())
+            .serialize(EVENT))
+            .isEqualTo(EVENT_JSON);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
new file mode 100644
index 0000000..3ea097a
--- /dev/null
+++ b/mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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.mailbox.quota.cassandra.listeners;
+
+import org.apache.james.eventsourcing.cassandra.CassandraEventStoreExtension;
+import org.apache.james.mailbox.quota.mailing.listeners.QuotaThresholdMailingIntegrationTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(CassandraEventStoreExtension.class)
+public class CassandraQuotaMailingListenersIntegrationTest implements QuotaThresholdMailingIntegrationTest {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java b/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
index 91cdbb9..11c8e46 100644
--- a/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
+++ b/mailbox/plugin/quota-mailing-memory/src/main/java/org/apache/james/eventsource/InMemoryEventStore.java
@@ -54,21 +54,14 @@ public class InMemoryEventStore implements EventStore {
 
     private AggregateId getAggregateId(List<? extends Event> events) {
         Preconditions.checkArgument(!events.isEmpty());
-        Preconditions.checkArgument(belongsToSameAggregate(events));
+        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
+
         return events.stream()
             .map(Event::getAggregateId)
             .findFirst()
             .get();
     }
 
-    private boolean belongsToSameAggregate(List<? extends Event> events) {
-        return events.stream()
-            .map(Event::getAggregateId)
-            .distinct()
-            .limit(2)
-            .count() <= 1;
-    }
-
     private void appendToEmptyHistory(AggregateId aggregateId, List<Event> events) {
         History newHistory = History.of(events);
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
index 7b3bc00..2b31374 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/eventsourcing/Event.java
@@ -19,8 +19,18 @@
 
 package org.apache.james.eventsourcing;
 
+import java.util.List;
+
 public interface Event extends Comparable<Event> {
 
+    static boolean belongsToSameAggregate(List<? extends Event> events) {
+        return events.stream()
+            .map(Event::getAggregateId)
+            .distinct()
+            .limit(2)
+            .count() == 1;
+    }
+
     EventId eventId();
 
     AggregateId getAggregateId();
@@ -29,4 +39,5 @@ public interface Event extends Comparable<Event> {
     default int compareTo(Event o) {
         return eventId().compareTo(o.eventId());
     }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/events/QuotaThresholdChangedEvent.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/events/QuotaThresholdChangedEvent.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/events/QuotaThresholdChangedEvent.java
index 3c21a3f..1c036f7 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/events/QuotaThresholdChangedEvent.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/events/QuotaThresholdChangedEvent.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.mailbox.quota.mailing.events;
 
+import java.util.Objects;
+
 import org.apache.james.eventsourcing.Event;
 import org.apache.james.eventsourcing.EventId;
 import org.apache.james.mailbox.model.Quota;
@@ -71,5 +73,23 @@ public class QuotaThresholdChangedEvent implements Event {
         return aggregateId;
     }
 
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof QuotaThresholdChangedEvent) {
+            QuotaThresholdChangedEvent that = (QuotaThresholdChangedEvent) o;
+
+            return Objects.equals(this.eventId, that.eventId)
+                && Objects.equals(this.sizeHistoryEvolution, that.sizeHistoryEvolution)
+                && Objects.equals(this.countHistoryEvolution, that.countHistoryEvolution)
+                && Objects.equals(this.sizeQuota, that.sizeQuota)
+                && Objects.equals(this.countQuota, that.countQuota)
+                && Objects.equals(this.aggregateId, that.aggregateId);
+        }
+        return false;
+    }
 
+    @Override
+    public final int hashCode() {
+        return Objects.hash(eventId, sizeHistoryEvolution, countHistoryEvolution, sizeQuota, countQuota, aggregateId);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c3a1bda2/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/HistoryEvolution.java
----------------------------------------------------------------------
diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/HistoryEvolution.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/HistoryEvolution.java
index e7cdf14..7f71025 100644
--- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/HistoryEvolution.java
+++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/HistoryEvolution.java
@@ -22,13 +22,14 @@ package org.apache.james.mailbox.quota.model;
 import java.util.Objects;
 import java.util.Optional;
 
+import com.google.common.base.MoreObjects;
+
 public class HistoryEvolution {
 
     public static HistoryEvolution noChanges() {
         return new HistoryEvolution(HistoryChangeType.NoChange,
             Optional.empty(),
-            Optional.empty()
-            );
+            Optional.empty());
     }
 
     public static HistoryEvolution lowerThresholdReached(QuotaThresholdChange currentThreshold) {
@@ -58,7 +59,7 @@ public class HistoryEvolution {
     private final Optional<HighestThresholdRecentness> recentness;
     private final Optional<QuotaThresholdChange> thresholdChange;
 
-    private HistoryEvolution(HistoryChangeType thresholdHistoryChange, Optional<HighestThresholdRecentness> recentness, Optional<QuotaThresholdChange> thresholdChange) {
+    public HistoryEvolution(HistoryChangeType thresholdHistoryChange, Optional<HighestThresholdRecentness> recentness, Optional<QuotaThresholdChange> thresholdChange) {
         this.thresholdHistoryChange = thresholdHistoryChange;
         this.recentness = recentness;
         this.thresholdChange = thresholdChange;
@@ -82,6 +83,10 @@ public class HistoryEvolution {
         return thresholdHistoryChange;
     }
 
+    public Optional<HighestThresholdRecentness> getRecentness() {
+        return recentness;
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof HistoryEvolution) {
@@ -99,13 +104,12 @@ public class HistoryEvolution {
         return Objects.hash(thresholdHistoryChange, recentness, thresholdChange);
     }
 
-
     @Override
     public String toString() {
-        return "HistoryEvolution{" +
-            "thresholdHistoryChange=" + thresholdHistoryChange +
-            ", recentness=" + recentness +
-            ", thresholdChange=" + thresholdChange +
-            '}';
+        return MoreObjects.toStringHelper(this)
+            .add("thresholdHistoryChange", thresholdHistoryChange)
+            .add("recentness", recentness)
+            .add("thresholdChange", thresholdChange)
+            .toString();
     }
 }


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


Mime
View raw message