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 <matth...@apache.org> Authored: Mon May 7 18:05:45 2018 +0200 Committer: benwa <btell...@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("f...@bar.com"))); + + public static final String EVENT_JSON = "{" + + " \"type\": \"quota-threshold-change\"," + + " \"eventId\": 0," + + " \"user\": \"f...@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-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org