From c3a1bda2d0bd84c936b5848d06ec6fd9a176868c Mon Sep 17 00:00:00 2001 From: Matthieu Baechler Date: Mon, 7 May 2018 18:05:45 +0200 Subject: [PATCH] MAILBOX-332 Cassandra implementation for Quota Threshold Crossing notification --- .../cassandra/DockerCassandraExtension.java | 4 + .../james/mailbox/quota/QuotaCount.java | 6 +- .../apache/james/mailbox/quota/QuotaSize.java | 6 +- .../plugin/quota-mailing-cassandra/pom.xml | 135 ++++++++++++++++ .../cassandra/CassandraEventStore.java | 62 ++++++++ .../cassandra/CassandraEventStoreModule.java | 62 ++++++++ .../cassandra/CassandraEventStoreTable.java | 27 ++++ .../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 ++++ .../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 ++++++++++++++++++ ...aQuotaMailingListenersIntegrationTest.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 + .../EventSourcingSystemTest.java | 3 +- .../james/eventsourcing/EventStoreTest.java | 2 +- .../james/eventsourcing/TestAggregateId.java | 4 + mailbox/pom.xml | 1 + 32 files changed, 1488 insertions(+), 23 deletions(-) create mode 100644 mailbox/plugin/quota-mailing-cassandra/pom.xml create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStore.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreModule.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTable.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/EventStoreDao.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializer.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTO.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/eventsourcing/cassandra/dto/EventDTOModule.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/HistoryEvolutionDTO.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaDTO.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTO.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/main/java/org/apache/james/mailbox/quota/cassandra/dto/QuotaThresholdChangedEventDTOModule.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventSourcingSystemTest.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreExtension.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/CassandraEventStoreTest.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/JsonEventSerializerTest.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTO.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/eventsourcing/cassandra/dto/TestEventDTOModule.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/dto/DTOTest.java create mode 100644 mailbox/plugin/quota-mailing-cassandra/src/test/java/org/apache/james/mailbox/quota/cassandra/listeners/CassandraQuotaMailingListenersIntegrationTest.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 43a3cb518f9..add70c8f2c0 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 boolean supportsParameter(ParameterContext parameterContext, ExtensionCon public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return dockerCassandra; } + + public DockerCassandra getDockerCassandra() { + return dockerCassandra; + } public static class DockerCassandra { 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 a932c3343c7..ac17fa0176b 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 static QuotaCount unlimited() { } public static QuotaCount count(long value) { - return new QuotaCount(Optional.of(value)); + return count(Optional.of(value)); + } + + public static QuotaCount count(Optional value) { + return new QuotaCount(value); } private final Optional value; 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 c8fb1a08f16..4d16e526563 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 static QuotaSize unlimited() { } public static QuotaSize size(long value) { - return new QuotaSize(Optional.of(value)); + return size(Optional.of(value)); + } + + public static QuotaSize size(Optional value) { + return new QuotaSize(value); } private final Optional value; diff --git a/mailbox/plugin/quota-mailing-cassandra/pom.xml b/mailbox/plugin/quota-mailing-cassandra/pom.xml new file mode 100644 index 00000000000..38dbda20da1 --- /dev/null +++ b/mailbox/plugin/quota-mailing-cassandra/pom.xml @@ -0,0 +1,135 @@ + + + + 4.0.0 + + + apache-james-mailbox + org.apache.james + 3.1.0-SNAPSHOT + ../../pom.xml + + + quota-mailing-cassandra + Apache James :: Mailbox :: Plugin :: Quota Mailing :: Cassandra + Apache James Mailbox Cassandra implementation of Quota mailing listener + + + + ${project.groupId} + apache-james-backends-cassandra + + + ${project.groupId} + apache-james-backends-cassandra + test-jar + test + + + ${project.groupId} + apache-james-mailbox-api + test-jar + test + + + ${project.groupId} + apache-james-mailbox-quota-mailing + + + ${project.groupId} + apache-james-mailbox-quota-mailing + test-jar + test + + + ${project.groupId} + apache-james-mailbox-store + test + + + ${project.groupId} + apache-mailet-base + test-jar + test + + + ${project.groupId} + james-server-data-memory + test + + + ch.qos.logback + logback-classic + test + + + org.assertj + assertj-core + test + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + + net.javacrumbs.json-unit + json-unit + 1.5.5 + test + + + net.javacrumbs.json-unit + json-unit-fluent + test + + + nl.jqno.equalsverifier + equalsverifier + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-launcher + test + + + org.mockito + mockito-core + test + + + org.testcontainers + testcontainers + test + + + + 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 00000000000..61c5a06a0de --- /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 events) { + if (events.isEmpty()) { + return; + } + doAppendAll(events); + } + + public void doAppendAll(List 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); + } +} 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 00000000000..ed15cb296b2 --- /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 tables; + private final List 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 moduleTables() { + return tables; + } + + @Override + public List moduleTypes() { + return types; + } + +} 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 00000000000..c90b81e9ccb --- /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"; +} 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 00000000000..2518f023020 --- /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 appendAll(List 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 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); + } + } +} 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 00000000000..bf3a5dcb770 --- /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, EventDTOModule> eventClassToModule; + private final Map typeToModule; + private final ObjectMapper objectMapper; + + @Inject + public JsonEventSerializer(Set 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 retrieveDTOClass(String type) { + return Optional.ofNullable(typeToModule.get(type)) + .map(EventDTOModule::getDTOClass) + .orElseThrow(() -> new UnknownEventException("unknown event type " + type)); + } + +} 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 00000000000..4616248fc68 --- /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(); +} 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 00000000000..d86d1d762b7 --- /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 getDTOClass(); + + Class getEventClass(); + + EventDTO toDTO(Event event); +} 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 00000000000..2d1dda1cc08 --- /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 recentness; + private final Optional threshold; + private final Optional instant; + + @JsonCreator + public HistoryEvolutionDTO( + @JsonProperty("changeType") HistoryEvolution.HistoryChangeType change, + @JsonProperty("recentness") Optional recentness, + @JsonProperty("threshold") Optional threshold, + @JsonProperty("instant") Optional instant) { + this.change = change; + this.recentness = recentness; + this.threshold = threshold; + this.instant = instant; + } + + public HistoryEvolution.HistoryChangeType getChange() { + return change; + } + + public Optional getRecentness() { + return recentness; + } + + public Optional getThreshold() { + return threshold; + } + + public Optional 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 = threshold + .map(QuotaThreshold::new) + .map(value -> new QuotaThresholdChange(value, Instant.ofEpochMilli(instant.get()))); + + return new HistoryEvolution( + change, + recentness, + quotaThresholdChange); + + } +} 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 00000000000..bf204c8a616 --- /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 limit; + + @JsonCreator + private QuotaDTO(@JsonProperty("used") long used, + @JsonProperty("limit") Optional limit) { + this.used = used; + this.limit = limit; + } + + public long getUsed() { + return used; + } + + public Optional getLimit() { + return limit; + } + + @JsonIgnore + public Quota asSizeQuota() { + return Quota.builder() + .used(QuotaSize.size(used)) + .computedLimit(QuotaSize.size(limit)) + .build(); + } + + @JsonIgnore + public Quota asCountQuota() { + return Quota.builder() + .used(QuotaCount.count(used)) + .computedLimit(QuotaCount.count(limit)) + .build(); + } +} 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 00000000000..80eae8d53cc --- /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))); + } +} 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 00000000000..328cb09c2bd --- /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 getDTOClass() { + return QuotaThresholdChangedEventDTO.class; + } + + @Override + public Class getEventClass() { + return QuotaThresholdChangedEvent.class; + } + + @Override + public EventDTO toDTO(Event event) { + Preconditions.checkArgument(event instanceof QuotaThresholdChangedEvent); + return QuotaThresholdChangedEventDTO.from( + (QuotaThresholdChangedEvent) event, + QUOTA_THRESHOLD_CHANGE); + } +} 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 00000000000..bee6e88a57e --- /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 { + +} 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 00000000000..1b0066f8f7d --- /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); + } +} 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 00000000000..314b1852093 --- /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 { + +} 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 00000000000..893647bab8f --- /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 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 00000000000..529d8a9a5a7 --- /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); + } +} 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 00000000000..6b462682699 --- /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 getDTOClass() { + return TestEventDTO.class; + } + + @Override + public Class 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()); + } +} 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 00000000000..a42725ac4eb --- /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 SIZE_QUOTA = Quota.builder().used(QuotaSize.size(23)).computedLimit(QuotaSize.size(33)).build(); + public static final Quota COUNT_QUOTA = Quota.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 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 00000000000..3ea097ac890 --- /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 { + +} 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 91cdbb921e9..11c8e46e762 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 void appendAll(List events) { private AggregateId getAggregateId(List 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 events) { - return events.stream() - .map(Event::getAggregateId) - .distinct() - .limit(2) - .count() <= 1; - } - private void appendToEmptyHistory(AggregateId aggregateId, List events) { History newHistory = History.of(events); 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 7b3bc00d4c5..2b3137438c8 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 { + static boolean belongsToSameAggregate(List 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 { default int compareTo(Event o) { return eventId().compareTo(o.eventId()); } + } 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 3c21a3f5e9e..1c036f7330b 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 UserQuotaThresholds.Id getAggregateId() { 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); + } } 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 e7cdf14a88a..7f710250e99 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 @@ 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 enum HighestThresholdRecentness { private final Optional recentness; private final Optional thresholdChange; - private HistoryEvolution(HistoryChangeType thresholdHistoryChange, Optional recentness, Optional thresholdChange) { + public HistoryEvolution(HistoryChangeType thresholdHistoryChange, Optional recentness, Optional thresholdChange) { this.thresholdHistoryChange = thresholdHistoryChange; this.recentness = recentness; this.thresholdChange = thresholdChange; @@ -82,6 +83,10 @@ public HistoryChangeType getThresholdHistoryChange() { return thresholdHistoryChange; } + public Optional getRecentness() { + return recentness; + } + @Override public final boolean equals(Object o) { if (o instanceof HistoryEvolution) { @@ -99,13 +104,12 @@ public final int hashCode() { 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(); } } diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistory.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistory.java index b80a39ff561..2e3d55126bd 100644 --- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistory.java +++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistory.java @@ -30,6 +30,7 @@ import java.util.Optional; import com.github.steveash.guavate.Guavate; +import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -106,4 +107,11 @@ public final boolean equals(Object o) { public final int hashCode() { return Objects.hash(changes); } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("changes", changes) + .toString(); + } } diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java index b2b26a3631d..f0feab28e85 100644 --- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java +++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventSourcingSystemTest.java @@ -36,8 +36,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; - -interface EventSourcingSystemTest { +public interface EventSourcingSystemTest { String PAYLOAD_1 = "payload1"; String PAYLOAD_2 = "payload2"; diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java index c847f814c45..aad8daa0704 100644 --- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java +++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/EventStoreTest.java @@ -11,7 +11,7 @@ import nl.jqno.equalsverifier.EqualsVerifier; -interface EventStoreTest { +public interface EventStoreTest { TestAggregateId AGGREGATE_1 = testId(1); TestAggregateId AGGREGATE_2 = testId(2); diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java index 1d7c8afde55..b3ae78c7bc9 100644 --- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java +++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/eventsourcing/TestAggregateId.java @@ -40,6 +40,10 @@ public String asAggregateKey() { return "TestAggregateId-" + id; } + public int getId() { + return id; + } + @Override public final boolean equals(Object o) { if (o instanceof TestAggregateId) { diff --git a/mailbox/pom.xml b/mailbox/pom.xml index 560d59b5b0d..43904697e0e 100644 --- a/mailbox/pom.xml +++ b/mailbox/pom.xml @@ -54,6 +54,7 @@ zoo-seq-provider plugin/quota-mailing + plugin/quota-mailing-cassandra plugin/quota-mailing-memory plugin/spamassassin