This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch postgresql
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/postgresql by this push:
new 490163ab9b JAMES-2586 Implement PostgresQuotaCurrentValueDAO (#1813)
490163ab9b is described below
commit 490163ab9be5624420dc5c099763179639db2265
Author: Trần Hồng Quân <[email protected]>
AuthorDate: Mon Nov 27 11:30:30 2023 +0700
JAMES-2586 Implement PostgresQuotaCurrentValueDAO (#1813)
---
.../components/CassandraQuotaCurrentValueDao.java | 64 +--------
.../quota/CassandraQuotaCurrentValueDaoTest.java | 8 +-
.../quota/PostgresQuotaCurrentValueDAO.java | 120 +++++++++++++++++
.../postgres/quota/PostgresQuotaModule.java | 59 +++++++++
.../quota/PostgresQuotaCurrentValueDAOTest.java | 147 +++++++++++++++++++++
.../apache/james/core/quota/QuotaCurrentValue.java | 53 ++++++++
.../quota/CassandraCurrentQuotaManagerV2.java | 9 +-
.../sieve/cassandra/CassandraSieveQuotaDAOV2.java | 8 +-
.../upload/CassandraUploadUsageRepository.java | 7 +-
9 files changed, 398 insertions(+), 77 deletions(-)
diff --git
a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/components/CassandraQuotaCurrentValueDao.java
b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/components/CassandraQuotaCurrentValueDao.java
index 2c77929750..c968a0c4d5 100644
---
a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/components/CassandraQuotaCurrentValueDao.java
+++
b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/components/CassandraQuotaCurrentValueDao.java
@@ -30,8 +30,6 @@ import static
org.apache.james.backends.cassandra.components.CassandraQuotaCurre
import static
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueTable.QUOTA_TYPE;
import static
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueTable.TABLE_NAME;
-import java.util.Objects;
-
import javax.inject.Inject;
import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
@@ -47,66 +45,12 @@ import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.update.Update;
-import com.google.common.base.MoreObjects;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class CassandraQuotaCurrentValueDao {
- public static class QuotaKey {
-
- public static QuotaKey of(QuotaComponent component, String identifier,
QuotaType quotaType) {
- return new QuotaKey(component, identifier, quotaType);
- }
-
- private final QuotaComponent quotaComponent;
- private final String identifier;
- private final QuotaType quotaType;
-
- public QuotaComponent getQuotaComponent() {
- return quotaComponent;
- }
-
- public String getIdentifier() {
- return identifier;
- }
-
- public QuotaType getQuotaType() {
- return quotaType;
- }
-
- private QuotaKey(QuotaComponent quotaComponent, String identifier,
QuotaType quotaType) {
- this.quotaComponent = quotaComponent;
- this.identifier = identifier;
- this.quotaType = quotaType;
- }
-
- @Override
- public final int hashCode() {
- return Objects.hash(quotaComponent, identifier, quotaType);
- }
-
- @Override
- public final boolean equals(Object o) {
- if (o instanceof QuotaKey) {
- QuotaKey other = (QuotaKey) o;
- return Objects.equals(quotaComponent, other.quotaComponent)
- && Objects.equals(identifier, other.identifier)
- && Objects.equals(quotaType, other.quotaType);
- }
- return false;
- }
-
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("quotaComponent", quotaComponent)
- .add("identifier", identifier)
- .add("quotaType", quotaType)
- .toString();
- }
- }
-
private static final Logger LOGGER =
LoggerFactory.getLogger(CassandraQuotaCurrentValueDao.class);
private final CassandraAsyncExecutor queryExecutor;
@@ -126,7 +70,7 @@ public class CassandraQuotaCurrentValueDao {
this.deleteQuotaCurrentValueStatement =
session.prepare(deleteQuotaCurrentValueStatement().build());
}
- public Mono<Void> increase(QuotaKey quotaKey, long amount) {
+ public Mono<Void> increase(QuotaCurrentValue.Key quotaKey, long amount) {
return queryExecutor.executeVoid(increaseStatement.bind()
.setString(QUOTA_COMPONENT,
quotaKey.getQuotaComponent().getValue())
.setString(IDENTIFIER, quotaKey.getIdentifier())
@@ -139,7 +83,7 @@ public class CassandraQuotaCurrentValueDao {
});
}
- public Mono<Void> decrease(QuotaKey quotaKey, long amount) {
+ public Mono<Void> decrease(QuotaCurrentValue.Key quotaKey, long amount) {
return queryExecutor.executeVoid(decreaseStatement.bind()
.setString(QUOTA_COMPONENT,
quotaKey.getQuotaComponent().getValue())
.setString(IDENTIFIER, quotaKey.getIdentifier())
@@ -152,7 +96,7 @@ public class CassandraQuotaCurrentValueDao {
});
}
- public Mono<QuotaCurrentValue> getQuotaCurrentValue(QuotaKey quotaKey) {
+ public Mono<QuotaCurrentValue> getQuotaCurrentValue(QuotaCurrentValue.Key
quotaKey) {
return
queryExecutor.executeSingleRow(getQuotaCurrentValueStatement.bind()
.setString(QUOTA_COMPONENT,
quotaKey.getQuotaComponent().getValue())
.setString(IDENTIFIER, quotaKey.getIdentifier())
@@ -160,7 +104,7 @@ public class CassandraQuotaCurrentValueDao {
.map(row -> convertRowToModel(row));
}
- public Mono<Void> deleteQuotaCurrentValue(QuotaKey quotaKey) {
+ public Mono<Void> deleteQuotaCurrentValue(QuotaCurrentValue.Key quotaKey) {
return
queryExecutor.executeVoid(deleteQuotaCurrentValueStatement.bind()
.setString(QUOTA_COMPONENT,
quotaKey.getQuotaComponent().getValue())
.setString(IDENTIFIER, quotaKey.getIdentifier())
diff --git
a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/quota/CassandraQuotaCurrentValueDaoTest.java
b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/quota/CassandraQuotaCurrentValueDaoTest.java
index baa6eea80b..344e272acb 100644
---
a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/quota/CassandraQuotaCurrentValueDaoTest.java
+++
b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/quota/CassandraQuotaCurrentValueDaoTest.java
@@ -24,10 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.apache.james.backends.cassandra.CassandraClusterExtension;
-import org.apache.james.backends.cassandra.components.CassandraModule;
import
org.apache.james.backends.cassandra.components.CassandraMutualizedQuotaModule;
import
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueDao;
-import
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueDao.QuotaKey;
import org.apache.james.core.quota.QuotaComponent;
import org.apache.james.core.quota.QuotaCurrentValue;
import org.apache.james.core.quota.QuotaType;
@@ -37,7 +35,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public class CassandraQuotaCurrentValueDaoTest {
- private static final QuotaKey QUOTA_KEY =
QuotaKey.of(QuotaComponent.MAILBOX, "[email protected]", QuotaType.SIZE);
+ private static final QuotaCurrentValue.Key QUOTA_KEY =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.SIZE);
private CassandraQuotaCurrentValueDao cassandraQuotaCurrentValueDao;
@@ -93,7 +91,7 @@ public class CassandraQuotaCurrentValueDaoTest {
@Test
void deleteQuotaCurrentValueShouldDeleteSuccessfully() {
- QuotaKey quotaKey = QuotaKey.of(QuotaComponent.MAILBOX,
"[email protected]", QuotaType.SIZE);
+ QuotaCurrentValue.Key quotaKey =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.SIZE);
cassandraQuotaCurrentValueDao.increase(quotaKey, 100L).block();
cassandraQuotaCurrentValueDao.deleteQuotaCurrentValue(quotaKey).block();
@@ -126,7 +124,7 @@ public class CassandraQuotaCurrentValueDaoTest {
@Test
void getQuotasByComponentShouldGetAllQuotaTypesSuccessfully() {
- QuotaKey countQuotaKey = QuotaKey.of(QuotaComponent.MAILBOX,
"[email protected]", QuotaType.COUNT);
+ QuotaCurrentValue.Key countQuotaKey =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.COUNT);
QuotaCurrentValue expectedQuotaSize =
QuotaCurrentValue.builder().quotaComponent(QUOTA_KEY.getQuotaComponent())
.identifier(QUOTA_KEY.getIdentifier()).quotaType(QUOTA_KEY.getQuotaType()).currentValue(100l).build();
diff --git
a/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAO.java
b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAO.java
new file mode 100644
index 0000000000..8f5c7eea6c
--- /dev/null
+++
b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAO.java
@@ -0,0 +1,120 @@
+/****************************************************************
+ * 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.backends.postgres.quota;
+
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.COMPONENT;
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.CURRENT_VALUE;
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.IDENTIFIER;
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.PRIMARY_KEY_CONSTRAINT_NAME;
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.TABLE_NAME;
+import static
org.apache.james.backends.postgres.quota.PostgresQuotaModule.PostgresQuotaCurrentValueTable.TYPE;
+
+import java.util.function.Function;
+
+import org.apache.james.backends.postgres.utils.PostgresExecutor;
+import org.apache.james.core.quota.QuotaComponent;
+import org.apache.james.core.quota.QuotaCurrentValue;
+import org.apache.james.core.quota.QuotaType;
+import org.jooq.Record;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class PostgresQuotaCurrentValueDAO {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PostgresQuotaCurrentValueDAO.class);
+
+ private final PostgresExecutor postgresExecutor;
+
+ public PostgresQuotaCurrentValueDAO(PostgresExecutor postgresExecutor) {
+ this.postgresExecutor = postgresExecutor;
+ }
+
+ public Mono<Void> increase(QuotaCurrentValue.Key quotaKey, long amount) {
+ return postgresExecutor.executeVoid(dslContext ->
Mono.from(dslContext.insertInto(TABLE_NAME)
+ .set(IDENTIFIER, quotaKey.getIdentifier())
+ .set(COMPONENT, quotaKey.getQuotaComponent().getValue())
+ .set(TYPE, quotaKey.getQuotaType().getValue())
+ .set(CURRENT_VALUE, amount)
+ .onConflictOnConstraint(PRIMARY_KEY_CONSTRAINT_NAME)
+ .doUpdate()
+ .set(CURRENT_VALUE, CURRENT_VALUE.plus(amount))))
+ .onErrorResume(ex -> {
+ LOGGER.warn("Failure when increasing {} {} quota for {}. Quota
current value is thus not updated and needs re-computation",
+ quotaKey.getQuotaComponent().getValue(),
quotaKey.getQuotaType().getValue(), quotaKey.getIdentifier(), ex);
+ return Mono.empty();
+ });
+ }
+
+ public Mono<Void> decrease(QuotaCurrentValue.Key quotaKey, long amount) {
+ return postgresExecutor.executeVoid(dslContext ->
Mono.from(dslContext.insertInto(TABLE_NAME)
+ .set(IDENTIFIER, quotaKey.getIdentifier())
+ .set(COMPONENT, quotaKey.getQuotaComponent().getValue())
+ .set(TYPE, quotaKey.getQuotaType().getValue())
+ .set(CURRENT_VALUE, 0L)
+ .onConflictOnConstraint(PRIMARY_KEY_CONSTRAINT_NAME)
+ .doUpdate()
+ .set(CURRENT_VALUE, CURRENT_VALUE.minus(amount))))
+ .onErrorResume(ex -> {
+ LOGGER.warn("Failure when decreasing {} {} quota for {}. Quota
current value is thus not updated and needs re-computation",
+ quotaKey.getQuotaComponent().getValue(),
quotaKey.getQuotaType().getValue(), quotaKey.getIdentifier(), ex);
+ return Mono.empty();
+ });
+ }
+
+ public Mono<QuotaCurrentValue> getQuotaCurrentValue(QuotaCurrentValue.Key
quotaKey) {
+ return postgresExecutor.executeRow(dslContext ->
Mono.from(dslContext.select(CURRENT_VALUE)
+ .from(TABLE_NAME)
+ .where(IDENTIFIER.eq(quotaKey.getIdentifier()),
+ COMPONENT.eq(quotaKey.getQuotaComponent().getValue()),
+ TYPE.eq(quotaKey.getQuotaType().getValue()))))
+ .map(toQuotaCurrentValue(quotaKey));
+ }
+
+ public Mono<Void> deleteQuotaCurrentValue(QuotaCurrentValue.Key quotaKey) {
+ return postgresExecutor.executeVoid(dslContext ->
Mono.from(dslContext.deleteFrom(TABLE_NAME)
+ .where(IDENTIFIER.eq(quotaKey.getIdentifier()),
+ COMPONENT.eq(quotaKey.getQuotaComponent().getValue()),
+ TYPE.eq(quotaKey.getQuotaType().getValue()))));
+ }
+
+ public Flux<QuotaCurrentValue> getQuotaCurrentValues(QuotaComponent
quotaComponent, String identifier) {
+ return postgresExecutor.executeRows(dslContext ->
Flux.from(dslContext.select(TYPE, CURRENT_VALUE)
+ .from(TABLE_NAME)
+ .where(IDENTIFIER.eq(identifier),
+ COMPONENT.eq(quotaComponent.getValue()))))
+ .map(toQuotaCurrentValue(quotaComponent, identifier));
+ }
+
+ private Function<Record, QuotaCurrentValue>
toQuotaCurrentValue(QuotaCurrentValue.Key quotaKey) {
+ return record ->
QuotaCurrentValue.builder().quotaComponent(quotaKey.getQuotaComponent())
+ .identifier(quotaKey.getIdentifier())
+ .quotaType(quotaKey.getQuotaType())
+ .currentValue(record.get(CURRENT_VALUE)).build();
+ }
+
+ private static Function<Record, QuotaCurrentValue>
toQuotaCurrentValue(QuotaComponent quotaComponent, String identifier) {
+ return record ->
QuotaCurrentValue.builder().quotaComponent(quotaComponent)
+ .identifier(identifier)
+ .quotaType(QuotaType.of(record.get(TYPE)))
+ .currentValue(record.get(CURRENT_VALUE)).build();
+ }
+}
diff --git
a/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaModule.java
b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaModule.java
new file mode 100644
index 0000000000..dad84108d0
--- /dev/null
+++
b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/quota/PostgresQuotaModule.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * 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.backends.postgres.quota;
+
+import static org.jooq.impl.DSL.name;
+import static org.jooq.impl.SQLDataType.BIGINT;
+
+import org.apache.james.backends.postgres.PostgresModule;
+import org.apache.james.backends.postgres.PostgresTable;
+import org.jooq.Field;
+import org.jooq.Name;
+import org.jooq.Record;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+
+public interface PostgresQuotaModule {
+ interface PostgresQuotaCurrentValueTable {
+ Table<Record> TABLE_NAME = DSL.table("quota_current_value");
+
+ Field<String> IDENTIFIER = DSL.field("identifier",
SQLDataType.VARCHAR.notNull());
+ Field<String> COMPONENT = DSL.field("component",
SQLDataType.VARCHAR.notNull());
+ Field<String> TYPE = DSL.field("type", SQLDataType.VARCHAR.notNull());
+ Field<Long> CURRENT_VALUE = DSL.field(name(TABLE_NAME.getName(),
"current_value"), BIGINT.notNull());
+
+ Name PRIMARY_KEY_CONSTRAINT_NAME =
DSL.name("quota_current_value_primary_key");
+
+ PostgresTable TABLE = PostgresTable.name(TABLE_NAME.getName())
+ .createTableStep(((dsl, tableName) ->
dsl.createTableIfNotExists(tableName)
+ .column(IDENTIFIER)
+ .column(COMPONENT)
+ .column(TYPE)
+ .column(CURRENT_VALUE)
+ .constraint(DSL.constraint(PRIMARY_KEY_CONSTRAINT_NAME)
+ .primaryKey(IDENTIFIER, COMPONENT, TYPE))))
+ .disableRowLevelSecurity();
+ }
+
+ PostgresModule MODULE = PostgresModule.builder()
+ .addTable(PostgresQuotaCurrentValueTable.TABLE)
+ .build();
+}
diff --git
a/backends-common/postgres/src/test/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAOTest.java
b/backends-common/postgres/src/test/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAOTest.java
new file mode 100644
index 0000000000..0164d3bab6
--- /dev/null
+++
b/backends-common/postgres/src/test/java/org/apache/james/backends/postgres/quota/PostgresQuotaCurrentValueDAOTest.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.backends.postgres.quota;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.apache.james.backends.postgres.PostgresExtension;
+import org.apache.james.core.quota.QuotaComponent;
+import org.apache.james.core.quota.QuotaCurrentValue;
+import org.apache.james.core.quota.QuotaType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class PostgresQuotaCurrentValueDAOTest {
+ @RegisterExtension
+ static PostgresExtension postgresExtension =
PostgresExtension.withoutRowLevelSecurity(PostgresQuotaModule.MODULE);
+
+ private static final QuotaCurrentValue.Key QUOTA_KEY =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.SIZE);
+
+ private PostgresQuotaCurrentValueDAO postgresQuotaCurrentValueDAO;
+
+ @BeforeEach
+ void setup() {
+ postgresQuotaCurrentValueDAO = new
PostgresQuotaCurrentValueDAO(postgresExtension.getPostgresExecutor());
+ }
+
+ @Test
+ void increaseQuotaCurrentValueShouldCreateNewRowSuccessfully() {
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(100L);
+ }
+
+ @Test
+ void
increaseQuotaCurrentValueShouldCreateNewRowSuccessfullyWhenIncreaseAmountIsZero()
{
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 0L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isZero();
+ }
+
+ @Test
+ void increaseQuotaCurrentValueShouldIncreaseValueSuccessfully() {
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block()).isNull();
+
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(200L);
+ }
+
+ @Test
+ void
increaseQuotaCurrentValueShouldDecreaseValueSuccessfullyWhenIncreaseAmountIsNegative()
{
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 200L).block();
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, -100L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(100L);
+ }
+
+ @Test
+ void decreaseQuotaCurrentValueShouldDecreaseValueSuccessfully() {
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 200L).block();
+ postgresQuotaCurrentValueDAO.decrease(QUOTA_KEY, 100L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(100L);
+ }
+
+ @Test
+ void decreaseQuotaCurrentValueDownToNegativeShouldAllowNegativeValue() {
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+ postgresQuotaCurrentValueDAO.decrease(QUOTA_KEY, 1000L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(-900L);
+ }
+
+ @Test
+ void
decreaseQuotaCurrentValueWhenNoRecordYetShouldNotFailAndSetValueToZero() {
+ postgresQuotaCurrentValueDAO.decrease(QUOTA_KEY, 1000L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isZero();
+ }
+
+ @Test
+ void deleteQuotaCurrentValueShouldDeleteSuccessfully() {
+ QuotaCurrentValue.Key quotaKey =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.SIZE);
+ postgresQuotaCurrentValueDAO.increase(quotaKey, 100L).block();
+ postgresQuotaCurrentValueDAO.deleteQuotaCurrentValue(quotaKey).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(quotaKey).block())
+ .isNull();
+ }
+
+ @Test
+ void deleteQuotaCurrentValueShouldResetCounterForever() {
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+
postgresQuotaCurrentValueDAO.deleteQuotaCurrentValue(QUOTA_KEY).block();
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+
+
assertThat(postgresQuotaCurrentValueDAO.getQuotaCurrentValue(QUOTA_KEY).block().getCurrentValue())
+ .isEqualTo(100L);
+ }
+
+ @Test
+ void getQuotasByComponentShouldGetAllQuotaTypesSuccessfully() {
+ QuotaCurrentValue.Key countQuotaKey =
QuotaCurrentValue.Key.of(QuotaComponent.MAILBOX, "[email protected]",
QuotaType.COUNT);
+
+ QuotaCurrentValue expectedQuotaSize =
QuotaCurrentValue.builder().quotaComponent(QUOTA_KEY.getQuotaComponent())
+
.identifier(QUOTA_KEY.getIdentifier()).quotaType(QUOTA_KEY.getQuotaType()).currentValue(100L).build();
+ QuotaCurrentValue expectedQuotaCount =
QuotaCurrentValue.builder().quotaComponent(countQuotaKey.getQuotaComponent())
+
.identifier(countQuotaKey.getIdentifier()).quotaType(countQuotaKey.getQuotaType()).currentValue(56L).build();
+
+ postgresQuotaCurrentValueDAO.increase(QUOTA_KEY, 100L).block();
+ postgresQuotaCurrentValueDAO.increase(countQuotaKey, 56L).block();
+
+ List<QuotaCurrentValue> actual =
postgresQuotaCurrentValueDAO.getQuotaCurrentValues(QUOTA_KEY.getQuotaComponent(),
QUOTA_KEY.getIdentifier())
+ .collectList()
+ .block();
+
+ assertThat(actual).containsExactlyInAnyOrder(expectedQuotaSize,
expectedQuotaCount);
+ }
+}
diff --git
a/core/src/main/java/org/apache/james/core/quota/QuotaCurrentValue.java
b/core/src/main/java/org/apache/james/core/quota/QuotaCurrentValue.java
index 682f10c7bc..c1b38bb819 100644
--- a/core/src/main/java/org/apache/james/core/quota/QuotaCurrentValue.java
+++ b/core/src/main/java/org/apache/james/core/quota/QuotaCurrentValue.java
@@ -26,6 +26,59 @@ import com.google.common.base.Preconditions;
public class QuotaCurrentValue {
+ public static class Key {
+
+ public static Key of(QuotaComponent component, String identifier,
QuotaType quotaType) {
+ return new Key(component, identifier, quotaType);
+ }
+
+ private final QuotaComponent quotaComponent;
+ private final String identifier;
+ private final QuotaType quotaType;
+
+ public QuotaComponent getQuotaComponent() {
+ return quotaComponent;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public QuotaType getQuotaType() {
+ return quotaType;
+ }
+
+ private Key(QuotaComponent quotaComponent, String identifier,
QuotaType quotaType) {
+ this.quotaComponent = quotaComponent;
+ this.identifier = identifier;
+ this.quotaType = quotaType;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(quotaComponent, identifier, quotaType);
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof Key) {
+ Key other = (Key) o;
+ return Objects.equals(quotaComponent, other.quotaComponent)
+ && Objects.equals(identifier, other.identifier)
+ && Objects.equals(quotaType, other.quotaType);
+ }
+ return false;
+ }
+
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("quotaComponent", quotaComponent)
+ .add("identifier", identifier)
+ .add("quotaType", quotaType)
+ .toString();
+ }
+ }
+
public static class Builder {
private QuotaComponent quotaComponent;
private String identifier;
diff --git
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManagerV2.java
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManagerV2.java
index 15746cd2e6..934eb4cde6 100644
---
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManagerV2.java
+++
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManagerV2.java
@@ -26,7 +26,6 @@ import java.util.function.Predicate;
import javax.inject.Inject;
import
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueDao;
-import
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueDao.QuotaKey;
import org.apache.james.core.quota.QuotaComponent;
import org.apache.james.core.quota.QuotaCountUsage;
import org.apache.james.core.quota.QuotaCurrentValue;
@@ -117,16 +116,16 @@ public class CassandraCurrentQuotaManagerV2 implements
CurrentQuotaManager {
});
}
- private QuotaKey asQuotaKeyCount(QuotaRoot quotaRoot) {
+ private QuotaCurrentValue.Key asQuotaKeyCount(QuotaRoot quotaRoot) {
return asQuotaKey(quotaRoot, QuotaType.COUNT);
}
- private QuotaKey asQuotaKeySize(QuotaRoot quotaRoot) {
+ private QuotaCurrentValue.Key asQuotaKeySize(QuotaRoot quotaRoot) {
return asQuotaKey(quotaRoot, QuotaType.SIZE);
}
- private QuotaKey asQuotaKey(QuotaRoot quotaRoot, QuotaType quotaType) {
- return QuotaKey.of(
+ private QuotaCurrentValue.Key asQuotaKey(QuotaRoot quotaRoot, QuotaType
quotaType) {
+ return QuotaCurrentValue.Key.of(
QuotaComponent.MAILBOX,
quotaRoot.asString(),
quotaType);
diff --git
a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveQuotaDAOV2.java
b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveQuotaDAOV2.java
index 98168db81e..d68165ef24 100644
---
a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveQuotaDAOV2.java
+++
b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveQuotaDAOV2.java
@@ -51,14 +51,14 @@ public class CassandraSieveQuotaDAOV2 implements
CassandraSieveQuotaDAO {
@Override
public Mono<Long> spaceUsedBy(Username username) {
- CassandraQuotaCurrentValueDao.QuotaKey quotaKey = asQuotaKey(username);
+ QuotaCurrentValue.Key quotaKey = asQuotaKey(username);
return
currentValueDao.getQuotaCurrentValue(quotaKey).map(QuotaCurrentValue::getCurrentValue)
.switchIfEmpty(Mono.just(0L));
}
- private CassandraQuotaCurrentValueDao.QuotaKey asQuotaKey(Username
username) {
- return CassandraQuotaCurrentValueDao.QuotaKey.of(
+ private QuotaCurrentValue.Key asQuotaKey(Username username) {
+ return QuotaCurrentValue.Key.of(
QUOTA_COMPONENT,
username.asString(),
QuotaType.SIZE);
@@ -66,7 +66,7 @@ public class CassandraSieveQuotaDAOV2 implements
CassandraSieveQuotaDAO {
@Override
public Mono<Void> updateSpaceUsed(Username username, long spaceUsed) {
- CassandraQuotaCurrentValueDao.QuotaKey quotaKey = asQuotaKey(username);
+ QuotaCurrentValue.Key quotaKey = asQuotaKey(username);
return currentValueDao.deleteQuotaCurrentValue(quotaKey)
.then(currentValueDao.increase(quotaKey, spaceUsed));
diff --git
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepository.java
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepository.java
index ebf61da7c5..6a4520615c 100644
---
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepository.java
+++
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepository.java
@@ -24,6 +24,7 @@ import javax.inject.Inject;
import
org.apache.james.backends.cassandra.components.CassandraQuotaCurrentValueDao;
import org.apache.james.core.Username;
import org.apache.james.core.quota.QuotaComponent;
+import org.apache.james.core.quota.QuotaCurrentValue;
import org.apache.james.core.quota.QuotaSizeUsage;
import org.apache.james.core.quota.QuotaType;
import org.apache.james.jmap.api.upload.UploadUsageRepository;
@@ -43,19 +44,19 @@ public class CassandraUploadUsageRepository implements
UploadUsageRepository {
@Override
public Mono<Void> increaseSpace(Username username, QuotaSizeUsage usage) {
- return
cassandraQuotaCurrentValueDao.increase(CassandraQuotaCurrentValueDao.QuotaKey.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE),
+ return
cassandraQuotaCurrentValueDao.increase(QuotaCurrentValue.Key.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE),
usage.asLong());
}
@Override
public Mono<Void> decreaseSpace(Username username, QuotaSizeUsage usage) {
- return
cassandraQuotaCurrentValueDao.decrease(CassandraQuotaCurrentValueDao.QuotaKey.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE),
+ return
cassandraQuotaCurrentValueDao.decrease(QuotaCurrentValue.Key.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE),
usage.asLong());
}
@Override
public Mono<QuotaSizeUsage> getSpaceUsage(Username username) {
- return
cassandraQuotaCurrentValueDao.getQuotaCurrentValue(CassandraQuotaCurrentValueDao.QuotaKey.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE))
+ return
cassandraQuotaCurrentValueDao.getQuotaCurrentValue(QuotaCurrentValue.Key.of(QuotaComponent.JMAP_UPLOADS,
username.asString(), QuotaType.SIZE))
.map(quotaCurrentValue ->
QuotaSizeUsage.size(quotaCurrentValue.getCurrentValue())).defaultIfEmpty(DEFAULT_QUOTA_SIZE_USAGE);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]