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

commit 2225bd5dda05b8a6023cf174b09a4174a4fa598d
Author: Benoit Tellier <btell...@linagora.com>
AuthorDate: Wed Jul 10 10:36:27 2019 +0200

    JAMES-2810 Implement StorageInformation DAO
    
    This allow a quick retrieval of storage information for a given user message
    and will be used as a building block of the 
CassandraDeletedMessageMetadataVault
---
 .../deleted-messages-vault-cassandra/pom.xml       |  74 ++++++++++++++
 .../metadata/DeletedMessageMetadataModule.java     |  53 ++++++++++
 .../vault/metadata/StorageInformationDAO.java      | 102 +++++++++++++++++++
 .../vault/metadata/StorageInformationDAOTest.java  | 113 +++++++++++++++++++++
 4 files changed, 342 insertions(+)

diff --git a/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml 
b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml
new file mode 100644
index 0000000..5f2e5cd
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>apache-james-mailbox</artifactId>
+        <groupId>org.apache.james</groupId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    
<artifactId>apache-james-mailbox-deleted-messages-vault-cassandra</artifactId>
+    <name>Apache James :: Mailbox :: Plugin :: Deleted Messages Vault :: 
Cassandra</name>
+    <description>Apache James Mailbox Deleted Messages Vault metadata on top 
of Cassandra</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-backends-cassandra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-backends-cassandra</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            
<artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git 
a/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageMetadataModule.java
 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageMetadataModule.java
new file mode 100644
index 0000000..6fff8a9
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageMetadataModule.java
@@ -0,0 +1,53 @@
+/****************************************************************
+ * 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.vault.metadata;
+
+import static com.datastax.driver.core.DataType.text;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+
+public interface DeletedMessageMetadataModule {
+
+    interface StorageInformationTable {
+        String TABLE = "storageInformation";
+
+        String OWNER = "owner";
+        String MESSAGE_ID = "messageId";
+        String BUCKET_NAME = "bucketName";
+        String BLOB_ID = "blobId";
+    }
+
+    CassandraModule MODULE = CassandraModule
+        .builder()
+
+        .table(StorageInformationTable.TABLE)
+        .comment("Holds storage information for deleted messages in the 
BlobStore based DeletedMessages vault")
+        .options(options -> options
+            .caching(SchemaBuilder.KeyCaching.ALL, SchemaBuilder.noRows()))
+        .statement(statement -> statement
+            .addPartitionKey(StorageInformationTable.OWNER, text())
+            .addPartitionKey(StorageInformationTable.MESSAGE_ID, text())
+            .addColumn(StorageInformationTable.BUCKET_NAME, text())
+            .addColumn(StorageInformationTable.BLOB_ID, text()))
+
+        .build();
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/StorageInformationDAO.java
 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/StorageInformationDAO.java
new file mode 100644
index 0000000..e434a8b
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/StorageInformationDAO.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.vault.metadata;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.delete;
+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.vault.metadata.DeletedMessageMetadataModule.StorageInformationTable.BLOB_ID;
+import static 
org.apache.james.vault.metadata.DeletedMessageMetadataModule.StorageInformationTable.BUCKET_NAME;
+import static 
org.apache.james.vault.metadata.DeletedMessageMetadataModule.StorageInformationTable.MESSAGE_ID;
+import static 
org.apache.james.vault.metadata.DeletedMessageMetadataModule.StorageInformationTable.OWNER;
+import static 
org.apache.james.vault.metadata.DeletedMessageMetadataModule.StorageInformationTable.TABLE;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.MessageId;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+
+import reactor.core.publisher.Mono;
+
+class StorageInformationDAO {
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement addStatement;
+    private final PreparedStatement removeStatement;
+    private final PreparedStatement readStatement;
+    private final BlobId.Factory blobIdFactory;
+
+    StorageInformationDAO(Session session, BlobId.Factory blobIdFactory) {
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        this.addStatement = prepareAdd(session);
+        this.removeStatement = prepareRemove(session);
+        this.readStatement = prepareRead(session);
+        this.blobIdFactory = blobIdFactory;
+    }
+
+    private PreparedStatement prepareRead(Session session) {
+        return session.prepare(select(BUCKET_NAME, BLOB_ID)
+            .from(TABLE)
+            .where(eq(OWNER, bindMarker(OWNER)))
+            .and(eq(MESSAGE_ID, bindMarker(MESSAGE_ID))));
+    }
+
+    private PreparedStatement prepareRemove(Session session) {
+        return session.prepare(delete().from(TABLE)
+            .where(eq(OWNER, bindMarker(OWNER)))
+            .and(eq(MESSAGE_ID, bindMarker(MESSAGE_ID))));
+    }
+
+    private PreparedStatement prepareAdd(Session session) {
+        return session.prepare(insertInto(TABLE)
+            .value(OWNER, bindMarker(OWNER))
+            .value(MESSAGE_ID, bindMarker(MESSAGE_ID))
+            .value(BUCKET_NAME, bindMarker(BUCKET_NAME))
+            .value(BLOB_ID, bindMarker(BLOB_ID)));
+    }
+
+    Mono<Void> referenceStorageInformation(User user, MessageId messageId, 
StorageInformation storageInformation) {
+        return cassandraAsyncExecutor.executeVoid(addStatement.bind()
+            .setString(OWNER, user.asString())
+            .setString(MESSAGE_ID, messageId.serialize())
+            .setString(BUCKET_NAME, 
storageInformation.getBucketName().asString())
+            .setString(BLOB_ID, storageInformation.getBlobId().asString()));
+    }
+
+    Mono<Void> deleteStorageInformation(User user, MessageId messageId) {
+        return cassandraAsyncExecutor.executeVoid(removeStatement.bind()
+            .setString(OWNER, user.asString())
+            .setString(MESSAGE_ID, messageId.serialize()));
+    }
+
+    Mono<StorageInformation> retrieveStorageInformation(User user, MessageId 
messageId) {
+        return cassandraAsyncExecutor.executeSingleRow(readStatement.bind()
+            .setString(OWNER, user.asString())
+            .setString(MESSAGE_ID, messageId.serialize()))
+            .map(row -> StorageInformation.builder()
+                    .bucketName(BucketName.of(row.getString(BUCKET_NAME)))
+                    .blobId(blobIdFactory.from(row.getString(BLOB_ID))));
+    }
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/StorageInformationDAOTest.java
 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/StorageInformationDAOTest.java
new file mode 100644
index 0000000..3a80502
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-cassandra/src/test/java/org/apache/james/vault/metadata/StorageInformationDAOTest.java
@@ -0,0 +1,113 @@
+/****************************************************************
+ * 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.vault.metadata;
+
+import static 
org.apache.james.vault.metadata.DeletedMessageMetadataModule.MODULE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import java.util.Optional;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.blob.api.HashBlobId;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class StorageInformationDAOTest {
+    private static final BucketName BUCKET_NAME = 
BucketName.of("deletedMessages-2019-06-01");
+    private static final BucketName BUCKET_NAME_2 = 
BucketName.of("deletedMessages-2019-07-01");
+    private static final HashBlobId.Factory BLOB_ID_FACTORY = new 
HashBlobId.Factory();
+    private static final User OWNER = User.fromUsername("owner");
+    private static final TestMessageId MESSAGE_ID = TestMessageId.of(36);
+    private static final BlobId BLOB_ID = new 
HashBlobId.Factory().from("05dcb33b-8382-4744-923a-bc593ad84d23");
+    private static final BlobId BLOB_ID_2 = new 
HashBlobId.Factory().from("05dcb33b-8382-4744-923a-bc593ad84d24");
+    private static final StorageInformation STORAGE_INFORMATION = 
StorageInformation.builder().bucketName(BUCKET_NAME).blobId(BLOB_ID);
+    private static final StorageInformation STORAGE_INFORMATION_2 = 
StorageInformation.builder().bucketName(BUCKET_NAME_2).blobId(BLOB_ID_2);
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new 
CassandraClusterExtension(MODULE);
+
+    private StorageInformationDAO testee;
+
+    @BeforeEach
+    void setUp(CassandraCluster cassandra) {
+        testee = new StorageInformationDAO(cassandra.getConf(), 
BLOB_ID_FACTORY);
+    }
+
+    @Test
+    void retrieveStorageInformationShouldReturnEmptyWhenNone() {
+        Optional<StorageInformation> storageInformation = 
testee.retrieveStorageInformation(OWNER, MESSAGE_ID).blockOptional();
+
+        assertThat(storageInformation).isEmpty();
+    }
+
+    @Test
+    void retrieveStorageInformationShouldReturnAddedValue() {
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION).block();
+
+        Optional<StorageInformation> storageInformation = 
testee.retrieveStorageInformation(OWNER, MESSAGE_ID).blockOptional();
+        assertThat(storageInformation).contains(STORAGE_INFORMATION);
+    }
+
+    @Test
+    void retrieveStorageInformationShouldReturnLatestAddedValue() {
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION).block();
+
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION_2).block();
+
+        Optional<StorageInformation> storageInformation = 
testee.retrieveStorageInformation(OWNER, MESSAGE_ID).blockOptional();
+        assertThat(storageInformation).contains(STORAGE_INFORMATION_2);
+    }
+
+    @Test
+    void retrieveStorageInformationShouldReturnEmptyWhenDeleted() {
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION).block();
+
+        testee.deleteStorageInformation(OWNER, MESSAGE_ID).block();
+
+        Optional<StorageInformation> storageInformation = 
testee.retrieveStorageInformation(OWNER, MESSAGE_ID).blockOptional();
+        assertThat(storageInformation).isEmpty();
+    }
+
+
+    @Test
+    void referenceStorageInformationShouldBeAllowedAfterADelete() {
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION).block();
+
+        testee.deleteStorageInformation(OWNER, MESSAGE_ID).block();
+
+        testee.referenceStorageInformation(OWNER, MESSAGE_ID, 
STORAGE_INFORMATION).block();
+
+        Optional<StorageInformation> storageInformation = 
testee.retrieveStorageInformation(OWNER, MESSAGE_ID).blockOptional();
+        assertThat(storageInformation).contains(STORAGE_INFORMATION);
+    }
+
+    @Test
+    void deleteStorageInformationShouldNotThrowWhenNone() {
+        assertThatCode(() -> testee.deleteStorageInformation(OWNER, 
MESSAGE_ID).block())
+            .doesNotThrowAnyException();
+    }
+}
\ No newline at end of file


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

Reply via email to