This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new cd94e5434e JAMES-3925 Define an API to store current values of JMAP 
uploads (#1699)
cd94e5434e is described below

commit cd94e5434e528fc076ebc3635ee4ab06cb2c0528
Author: hungphan227 <45198168+hungphan...@users.noreply.github.com>
AuthorDate: Sat Aug 26 12:53:29 2023 +0700

    JAMES-3925 Define an API to store current values of JMAP uploads (#1699)
---
 .../james/modules/data/CassandraJmapModule.java    |  3 +
 .../james/modules/mailbox/MemoryQuotaModule.java   |  3 +
 .../upload/CassandraUploadUsageRepository.java     | 67 +++++++++++++++++++
 .../upload/CassandraUploadUsageRepositoryTest.java | 48 ++++++++++++++
 .../jmap/api/upload/UploadUsageRepository.java     | 34 ++++++++++
 .../upload/InMemoryUploadUsageRepository.java      | 75 ++++++++++++++++++++++
 .../api/upload/UploadUsageRepositoryContract.scala | 50 +++++++++++++++
 .../upload/InMemoryUploadUsageRepositoryTest.java  | 41 ++++++++++++
 8 files changed, 321 insertions(+)

diff --git 
a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
 
b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
index e1b9584628..a6c0884c27 100644
--- 
a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
+++ 
b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
@@ -41,6 +41,7 @@ import 
org.apache.james.jmap.api.projections.MessageFastViewProjectionHealthChec
 import org.apache.james.jmap.api.pushsubscription.PushDeleteUserDataTaskStep;
 import org.apache.james.jmap.api.pushsubscription.PushSubscriptionRepository;
 import org.apache.james.jmap.api.upload.UploadRepository;
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
 import org.apache.james.jmap.cassandra.access.CassandraAccessModule;
 import org.apache.james.jmap.cassandra.access.CassandraAccessTokenRepository;
 import org.apache.james.jmap.cassandra.change.CassandraEmailChangeModule;
@@ -57,6 +58,7 @@ import 
org.apache.james.jmap.cassandra.projections.CassandraMessageFastViewProje
 import 
org.apache.james.jmap.cassandra.pushsubscription.CassandraPushSubscriptionModule;
 import 
org.apache.james.jmap.cassandra.pushsubscription.CassandraPushSubscriptionRepository;
 import org.apache.james.jmap.cassandra.upload.CassandraUploadRepository;
+import org.apache.james.jmap.cassandra.upload.CassandraUploadUsageRepository;
 import org.apache.james.jmap.cassandra.upload.UploadConfiguration;
 import org.apache.james.jmap.cassandra.upload.UploadDAO;
 import org.apache.james.jmap.cassandra.upload.UploadModule;
@@ -80,6 +82,7 @@ public class CassandraJmapModule extends AbstractModule {
         bind(CassandraUploadRepository.class).in(Scopes.SINGLETON);
         bind(UploadDAO.class).in(Scopes.SINGLETON);
         bind(UploadRepository.class).to(CassandraUploadRepository.class);
+        
bind(UploadUsageRepository.class).to(CassandraUploadUsageRepository.class);
         
bind(UploadConfiguration.class).toInstance(UploadConfiguration.SINGLETON);
 
         bind(CassandraCustomIdentityDAO.class).in(Scopes.SINGLETON);
diff --git 
a/server/container/guice/memory/src/main/java/org/apache/james/modules/mailbox/MemoryQuotaModule.java
 
b/server/container/guice/memory/src/main/java/org/apache/james/modules/mailbox/MemoryQuotaModule.java
index 1fd76c36f7..dc389d74d6 100644
--- 
a/server/container/guice/memory/src/main/java/org/apache/james/modules/mailbox/MemoryQuotaModule.java
+++ 
b/server/container/guice/memory/src/main/java/org/apache/james/modules/mailbox/MemoryQuotaModule.java
@@ -20,6 +20,8 @@
 package org.apache.james.modules.mailbox;
 
 import org.apache.james.events.EventListener;
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
+import org.apache.james.jmap.memory.upload.InMemoryUploadUsageRepository;
 import org.apache.james.mailbox.inmemory.quota.InMemoryCurrentQuotaManager;
 import org.apache.james.mailbox.inmemory.quota.InMemoryPerUserMaxQuotaManager;
 import org.apache.james.mailbox.quota.CurrentQuotaManager;
@@ -52,6 +54,7 @@ public class MemoryQuotaModule extends AbstractModule {
         bind(MaxQuotaManager.class).to(InMemoryPerUserMaxQuotaManager.class);
         bind(QuotaManager.class).to(StoreQuotaManager.class);
         bind(CurrentQuotaManager.class).to(InMemoryCurrentQuotaManager.class);
+        
bind(UploadUsageRepository.class).to(InMemoryUploadUsageRepository.class);
 
         bind(ListeningCurrentQuotaUpdater.class).in(Scopes.SINGLETON);
         bind(QuotaUpdater.class).to(ListeningCurrentQuotaUpdater.class);
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
new file mode 100644
index 0000000000..83f52adca1
--- /dev/null
+++ 
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepository.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.cassandra.upload;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.core.quota.QuotaComponent;
+import org.apache.james.core.quota.QuotaSizeUsage;
+import org.apache.james.core.quota.QuotaType;
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
+import org.apache.james.mailbox.cassandra.quota.CassandraQuotaCurrentValueDao;
+
+import reactor.core.publisher.Mono;
+
+public class CassandraUploadUsageRepository implements UploadUsageRepository {
+
+    private static final QuotaSizeUsage DEFAULT_QUOTA_SIZE_USAGE = 
QuotaSizeUsage.size(0);
+
+    private CassandraQuotaCurrentValueDao cassandraQuotaCurrentValueDao;
+
+    @Inject
+    public CassandraUploadUsageRepository(CassandraQuotaCurrentValueDao 
cassandraQuotaCurrentValueDao) {
+        this.cassandraQuotaCurrentValueDao = cassandraQuotaCurrentValueDao;
+    }
+
+    @Override
+    public Mono<Void> increaseSpace(Username username, QuotaSizeUsage usage) {
+        return 
cassandraQuotaCurrentValueDao.increase(CassandraQuotaCurrentValueDao.QuotaKey.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),
+            usage.asLong());
+    }
+
+    @Override
+    public Mono<QuotaSizeUsage> getSpaceUsage(Username username) {
+        return 
cassandraQuotaCurrentValueDao.getQuotaCurrentValue(CassandraQuotaCurrentValueDao.QuotaKey.of(QuotaComponent.JMAP_UPLOADS,
 username.asString(), QuotaType.SIZE))
+            .map(quotaCurrentValue -> 
QuotaSizeUsage.size(quotaCurrentValue.getCurrentValue())).defaultIfEmpty(DEFAULT_QUOTA_SIZE_USAGE);
+    }
+
+    @Override
+    public Mono<Void> resetSpace(Username username, QuotaSizeUsage usage) {
+        return getSpaceUsage(username).flatMap(quotaSizeUsage -> 
Mono.from(decreaseSpace(username, quotaSizeUsage)))
+            .then(increaseSpace(username, usage));
+    }
+}
diff --git 
a/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepositoryTest.java
 
b/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepositoryTest.java
new file mode 100644
index 0000000000..9eab33a494
--- /dev/null
+++ 
b/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/upload/CassandraUploadUsageRepositoryTest.java
@@ -0,0 +1,48 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.cassandra.upload;
+
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
+import org.apache.james.jmap.api.upload.UploadUsageRepositoryContract;
+import org.apache.james.mailbox.cassandra.modules.CassandraQuotaModule;
+import org.apache.james.mailbox.cassandra.quota.CassandraQuotaCurrentValueDao;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class CassandraUploadUsageRepositoryTest implements 
UploadUsageRepositoryContract {
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new 
CassandraClusterExtension(CassandraModule.aggregateModules(CassandraQuotaModule.MODULE));
+
+    private CassandraUploadUsageRepository cassandraUploadUsageRepository;
+
+    @BeforeEach
+    private void setup() {
+        cassandraUploadUsageRepository = new 
CassandraUploadUsageRepository(new 
CassandraQuotaCurrentValueDao(cassandraCluster.getCassandraCluster().getConf()));
+        resetCounterToZero();
+    }
+
+    @Override
+    public UploadUsageRepository uploadUsageRepository() {
+        return cassandraUploadUsageRepository;
+    }
+}
diff --git 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/upload/UploadUsageRepository.java
 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/upload/UploadUsageRepository.java
new file mode 100644
index 0000000000..614196dc7a
--- /dev/null
+++ 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/upload/UploadUsageRepository.java
@@ -0,0 +1,34 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.api.upload;
+
+import org.apache.james.core.Username;
+import org.apache.james.core.quota.QuotaSizeUsage;
+import org.reactivestreams.Publisher;
+
+public interface UploadUsageRepository {
+    Publisher<Void> increaseSpace(Username username, QuotaSizeUsage usage);
+
+    Publisher<Void> decreaseSpace(Username username, QuotaSizeUsage usage);
+
+    Publisher<QuotaSizeUsage> getSpaceUsage(Username username);
+
+    Publisher<Void> resetSpace(Username username, QuotaSizeUsage usage);
+}
diff --git 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepository.java
 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepository.java
new file mode 100644
index 0000000000..d0d682a839
--- /dev/null
+++ 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepository.java
@@ -0,0 +1,75 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.memory.upload;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.james.core.Username;
+import org.apache.james.core.quota.QuotaSizeUsage;
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
+
+import reactor.core.publisher.Mono;
+
+public class InMemoryUploadUsageRepository implements UploadUsageRepository {
+
+    private static final QuotaSizeUsage DEFAULT_QUOTA_SIZE_USAGE = 
QuotaSizeUsage.size(0);
+
+    private final Map<Username, AtomicReference<QuotaSizeUsage>> cache;
+
+    public InMemoryUploadUsageRepository() {
+        cache = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public Mono<Void> increaseSpace(Username username, QuotaSizeUsage usage) {
+        return updateSpace(username, usage.asLong());
+    }
+
+    @Override
+    public Mono<Void> decreaseSpace(Username username, QuotaSizeUsage usage) {
+        return updateSpace(username, Math.negateExact(usage.asLong()));
+    }
+
+    private Mono<Void> updateSpace(Username username, long amount) {
+        return Mono.fromRunnable(() -> {
+            AtomicReference<QuotaSizeUsage> quotaSizeUsageAtomicReference = 
cache.get(username);
+            if (Objects.isNull(quotaSizeUsageAtomicReference)) {
+                cache.put(username, new 
AtomicReference<>(QuotaSizeUsage.size(amount)));
+            } else {
+                quotaSizeUsageAtomicReference.updateAndGet(quotaSizeUsage -> 
quotaSizeUsage.add(amount));
+            }
+        });
+    }
+
+    @Override
+    public Mono<QuotaSizeUsage> getSpaceUsage(Username username) {
+        return Mono.just(cache.getOrDefault(username, new 
AtomicReference<>(DEFAULT_QUOTA_SIZE_USAGE)))
+            .map(quotaSizeUsageAtomicReference -> 
quotaSizeUsageAtomicReference.get());
+    }
+
+    @Override
+    public Mono<Void> resetSpace(Username username, QuotaSizeUsage usage) {
+        return getSpaceUsage(username).flatMap(quotaSizeUsage -> 
Mono.from(decreaseSpace(username, quotaSizeUsage)))
+            .then(increaseSpace(username, usage));
+    }
+}
diff --git 
a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/upload/UploadUsageRepositoryContract.scala
 
b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/upload/UploadUsageRepositoryContract.scala
new file mode 100644
index 0000000000..0ceff5890f
--- /dev/null
+++ 
b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/upload/UploadUsageRepositoryContract.scala
@@ -0,0 +1,50 @@
+package org.apache.james.jmap.api.upload
+
+import org.apache.james.core.Username
+import org.apache.james.core.quota.QuotaSizeUsage
+import org.apache.james.jmap.api.upload.UploadUsageRepositoryContract.USER_NAME
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import reactor.core.publisher.Mono
+import reactor.core.scala.publisher.SMono
+
+object UploadUsageRepositoryContract {
+  val USER_NAME = Username.of("ja...@abc.com")
+}
+
+trait UploadUsageRepositoryContract {
+  def uploadUsageRepository: UploadUsageRepository
+
+  def resetCounterToZero(): Unit = {
+    Mono.from(uploadUsageRepository.resetSpace(USER_NAME, 
QuotaSizeUsage.size(0))).block
+  }
+
+  @Test
+  def increaseSpaceShouldIncreaseSuccessfully(): Unit = {
+    SMono.fromPublisher(uploadUsageRepository.increaseSpace(USER_NAME, 
QuotaSizeUsage.size(100))).block();
+    val expected = 
SMono.fromPublisher(uploadUsageRepository.getSpaceUsage(USER_NAME)).block();
+    assertThat(expected.asLong()).isEqualTo(100);
+  }
+
+  @Test
+  def decreaseSpaceShouldDecreaseSuccessfully(): Unit = {
+    SMono.fromPublisher(uploadUsageRepository.increaseSpace(USER_NAME, 
QuotaSizeUsage.size(200))).block();
+    SMono.fromPublisher(uploadUsageRepository.decreaseSpace(USER_NAME, 
QuotaSizeUsage.size(100))).block();
+    val expected = 
SMono.fromPublisher(uploadUsageRepository.getSpaceUsage(USER_NAME)).block();
+    assertThat(expected.asLong()).isEqualTo(100);
+  }
+
+  @Test
+  def resetSpaceShouldResetSuccessfully(): Unit = {
+    SMono.fromPublisher(uploadUsageRepository.increaseSpace(USER_NAME, 
QuotaSizeUsage.size(200))).block();
+    SMono.fromPublisher(uploadUsageRepository.resetSpace(USER_NAME, 
QuotaSizeUsage.size(100))).block();
+    val expected = 
SMono.fromPublisher(uploadUsageRepository.getSpaceUsage(USER_NAME)).block();
+    assertThat(expected.asLong()).isEqualTo(100);
+  }
+
+  @Test
+  def getSpaceUsageShouldReturnZeroWhenRecordDoesNotExist(): Unit = {
+    
assertThat(SMono.fromPublisher(uploadUsageRepository.getSpaceUsage(Username.of("aaa"))).block().asLong()).isEqualTo(0);
+  }
+
+}
diff --git 
a/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepositoryTest.java
 
b/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepositoryTest.java
new file mode 100644
index 0000000000..db5a49d873
--- /dev/null
+++ 
b/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/upload/InMemoryUploadUsageRepositoryTest.java
@@ -0,0 +1,41 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.memory.upload;
+
+import org.apache.james.jmap.api.upload.UploadUsageRepository;
+import org.apache.james.jmap.api.upload.UploadUsageRepositoryContract;
+import org.junit.jupiter.api.BeforeEach;
+
+public class InMemoryUploadUsageRepositoryTest implements 
UploadUsageRepositoryContract {
+
+    private InMemoryUploadUsageRepository inMemoryUploadUsageRepository;
+
+    @BeforeEach
+    private void setup() {
+        inMemoryUploadUsageRepository = new InMemoryUploadUsageRepository();
+        resetCounterToZero();
+    }
+
+    @Override
+    public UploadUsageRepository uploadUsageRepository() {
+        return inMemoryUploadUsageRepository;
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to