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

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

commit c2dbfc579f41e6aef0475da24f804d9cb21b59d5
Author: Quan Tran <hqt...@linagora.com>
AuthorDate: Tue Jan 2 15:05:05 2024 +0700

    JAMES-2586 Implement PostgresDeletedMessageMetadataVault
---
 Jenkinsfile                                        |   3 +-
 .../plugin/deleted-messages-vault-postgres/pom.xml |  79 ++++++++++++++
 .../PostgresDeletedMessageMetadataModule.java      |  65 ++++++++++++
 .../PostgresDeletedMessageMetadataVault.java       | 115 +++++++++++++++++++++
 .../PostgresDeletedMessageMetadataVaultTest.java   |  46 +++++++++
 .../james/vault/metadata/MetadataSerializer.java   |   0
 mailbox/pom.xml                                    |   1 +
 pom.xml                                            |   5 +
 8 files changed, 313 insertions(+), 1 deletion(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index e27ed683f4..cb220daa6e 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -46,7 +46,8 @@ pipeline {
                 'server/container/guice/mailbox-postgres,' +
                 'server/apps/postgres-app,' +
                 'mpt/impl/imap-mailbox/postgres,' +
-                'event-bus/postgres'
+                'event-bus/postgres,' +
+                'mailbox/plugin/deleted-messages-vault-postgres'
     }
 
     tools {
diff --git a/mailbox/plugin/deleted-messages-vault-postgres/pom.xml 
b/mailbox/plugin/deleted-messages-vault-postgres/pom.xml
new file mode 100644
index 0000000000..103fd725b4
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault-postgres/pom.xml
@@ -0,0 +1,79 @@
+<?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>
+        <groupId>org.apache.james</groupId>
+        <artifactId>apache-james-mailbox</artifactId>
+        <version>3.9.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    
<artifactId>apache-james-mailbox-deleted-messages-vault-postgres</artifactId>
+    <name>Apache James :: Mailbox :: Plugin :: Deleted Messages Vault :: 
Postgres</name>
+    <description>Apache James Mailbox Deleted Messages Vault metadata on top 
of Postgres</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-postgres</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-postgres</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            
<artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            
<artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-common</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-testing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>testing-base</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>postgresql</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git 
a/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataModule.java
 
b/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataModule.java
new file mode 100644
index 0000000000..de041482a4
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataModule.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.OWNER_MESSAGE_ID_INDEX;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.TABLE;
+
+import org.apache.james.backends.postgres.PostgresIndex;
+import org.apache.james.backends.postgres.PostgresModule;
+import org.apache.james.backends.postgres.PostgresTable;
+import org.jooq.Field;
+import org.jooq.JSONB;
+import org.jooq.Record;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+
+public interface PostgresDeletedMessageMetadataModule {
+    interface DeletedMessageMetadataTable {
+        Table<Record> TABLE_NAME = DSL.table("deleted_messages_metadata");
+
+        Field<String> BUCKET_NAME = DSL.field("bucket_name", 
SQLDataType.VARCHAR.notNull());
+        Field<String> OWNER = DSL.field("owner", 
SQLDataType.VARCHAR.notNull());
+        Field<String> MESSAGE_ID = DSL.field("messageId", 
SQLDataType.VARCHAR.notNull());
+        Field<String> BLOB_ID = DSL.field("blob_id", 
SQLDataType.VARCHAR.notNull());
+        Field<JSONB> METADATA = DSL.field("metadata", 
SQLDataType.JSONB.notNull());
+
+        PostgresTable TABLE = PostgresTable.name(TABLE_NAME.getName())
+            .createTableStep(((dsl, tableName) -> 
dsl.createTableIfNotExists(tableName)
+                .column(BUCKET_NAME)
+                .column(OWNER)
+                .column(MESSAGE_ID)
+                .column(BLOB_ID)
+                .column(METADATA)
+                .primaryKey(BUCKET_NAME, OWNER, MESSAGE_ID)))
+            .disableRowLevelSecurity()
+            .build();
+
+        PostgresIndex OWNER_MESSAGE_ID_INDEX = 
PostgresIndex.name("owner_messageId_index")
+            .createIndexStep((dsl, indexName) -> 
dsl.createUniqueIndexIfNotExists(indexName)
+                .on(TABLE_NAME, OWNER, MESSAGE_ID));
+    }
+
+    PostgresModule MODULE = PostgresModule.builder()
+        .addTable(TABLE)
+        .addIndex(OWNER_MESSAGE_ID_INDEX)
+        .build();
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVault.java
 
b/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVault.java
new file mode 100644
index 0000000000..70bbd25476
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-postgres/src/main/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVault.java
@@ -0,0 +1,115 @@
+/****************************************************************
+ * 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.util.ReactorUtils.publishIfPresent;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.BLOB_ID;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.BUCKET_NAME;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.MESSAGE_ID;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.METADATA;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.OWNER;
+import static 
org.apache.james.vault.metadata.PostgresDeletedMessageMetadataModule.DeletedMessageMetadataTable.TABLE_NAME;
+import static org.jooq.JSONB.jsonb;
+
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.postgres.utils.PostgresExecutor;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.model.MessageId;
+import org.jooq.Record;
+import org.reactivestreams.Publisher;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class PostgresDeletedMessageMetadataVault implements 
DeletedMessageMetadataVault {
+    private final PostgresExecutor postgresExecutor;
+    private final MetadataSerializer metadataSerializer;
+    private final BlobId.Factory blobIdFactory;
+
+    @Inject
+    public PostgresDeletedMessageMetadataVault(PostgresExecutor 
postgresExecutor,
+                                               MetadataSerializer 
metadataSerializer,
+                                               BlobId.Factory blobIdFactory) {
+        this.postgresExecutor = postgresExecutor;
+        this.metadataSerializer = metadataSerializer;
+        this.blobIdFactory = blobIdFactory;
+    }
+
+    @Override
+    public Publisher<Void> store(DeletedMessageWithStorageInformation 
deletedMessage) {
+        return postgresExecutor.executeVoid(context -> 
Mono.from(context.insertInto(TABLE_NAME)
+            .set(OWNER, 
deletedMessage.getDeletedMessage().getOwner().asString())
+            .set(MESSAGE_ID, 
deletedMessage.getDeletedMessage().getMessageId().serialize())
+            .set(BUCKET_NAME, 
deletedMessage.getStorageInformation().getBucketName().asString())
+            .set(BLOB_ID, 
deletedMessage.getStorageInformation().getBlobId().asString())
+            .set(METADATA, 
jsonb(metadataSerializer.serialize(deletedMessage)))));
+    }
+
+    @Override
+    public Publisher<Void> removeMetadataRelatedToBucket(BucketName 
bucketName) {
+        return postgresExecutor.executeVoid(context -> 
Mono.from(context.deleteFrom(TABLE_NAME)
+            .where(BUCKET_NAME.eq(bucketName.asString()))));
+    }
+
+    @Override
+    public Publisher<Void> remove(BucketName bucketName, Username username, 
MessageId messageId) {
+        return postgresExecutor.executeVoid(context -> 
Mono.from(context.deleteFrom(TABLE_NAME)
+            .where(BUCKET_NAME.eq(bucketName.asString()),
+                OWNER.eq(username.asString()),
+                MESSAGE_ID.eq(messageId.serialize()))));
+    }
+
+    @Override
+    public Publisher<StorageInformation> retrieveStorageInformation(Username 
username, MessageId messageId) {
+        return postgresExecutor.executeRow(context -> 
Mono.from(context.select(BUCKET_NAME, BLOB_ID)
+            .from(TABLE_NAME)
+            .where(OWNER.eq(username.asString()),
+                MESSAGE_ID.eq(messageId.serialize()))))
+            .map(toStorageInformation());
+    }
+
+    private Function<Record, StorageInformation> toStorageInformation() {
+        return record -> StorageInformation.builder()
+            .bucketName(BucketName.of(record.get(BUCKET_NAME)))
+            .blobId(blobIdFactory.from(record.get(BLOB_ID)));
+    }
+
+    @Override
+    public Publisher<DeletedMessageWithStorageInformation> 
listMessages(BucketName bucketName, Username username) {
+        return postgresExecutor.executeRows(context -> 
Flux.from(context.select(METADATA)
+            .from(TABLE_NAME)
+            .where(BUCKET_NAME.eq(bucketName.asString()),
+                OWNER.eq(username.asString()))))
+            .map(record -> 
metadataSerializer.deserialize(record.get(METADATA).data()))
+            .handle(publishIfPresent());
+    }
+
+    @Override
+    public Publisher<BucketName> listRelatedBuckets() {
+        return postgresExecutor.executeRows(context -> 
Flux.from(context.selectDistinct(BUCKET_NAME)
+            .from(TABLE_NAME)))
+            .map(record -> BucketName.of(record.get(BUCKET_NAME)));
+    }
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault-postgres/src/test/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVaultTest.java
 
b/mailbox/plugin/deleted-messages-vault-postgres/src/test/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVaultTest.java
new file mode 100644
index 0000000000..766df623c3
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault-postgres/src/test/java/org/apache/james/vault/metadata/PostgresDeletedMessageMetadataVaultTest.java
@@ -0,0 +1,46 @@
+/****************************************************************
+ * 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 org.apache.james.backends.postgres.PostgresExtension;
+import org.apache.james.backends.postgres.PostgresModule;
+import org.apache.james.blob.api.HashBlobId;
+import org.apache.james.mailbox.inmemory.InMemoryId;
+import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import 
org.apache.james.vault.dto.DeletedMessageWithStorageInformationConverter;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class PostgresDeletedMessageMetadataVaultTest implements 
DeletedMessageMetadataVaultContract {
+    @RegisterExtension
+    static PostgresExtension postgresExtension = 
PostgresExtension.withoutRowLevelSecurity(
+        
PostgresModule.aggregateModules(PostgresDeletedMessageMetadataModule.MODULE));
+
+    @Override
+    public DeletedMessageMetadataVault metadataVault() {
+        HashBlobId.Factory blobIdFactory = new HashBlobId.Factory();
+        InMemoryMessageId.Factory messageIdFactory = new 
InMemoryMessageId.Factory();
+        DeletedMessageWithStorageInformationConverter dtoConverter = new 
DeletedMessageWithStorageInformationConverter(blobIdFactory,
+            messageIdFactory, new InMemoryId.Factory());
+
+        return new 
PostgresDeletedMessageMetadataVault(postgresExtension.getPostgresExecutor(),
+            new MetadataSerializer(dtoConverter),
+            blobIdFactory);
+    }
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/MetadataSerializer.java
 
b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/metadata/MetadataSerializer.java
similarity index 100%
rename from 
mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/MetadataSerializer.java
rename to 
mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/metadata/MetadataSerializer.java
diff --git a/mailbox/pom.xml b/mailbox/pom.xml
index 629c089807..0fe0776bc0 100644
--- a/mailbox/pom.xml
+++ b/mailbox/pom.xml
@@ -49,6 +49,7 @@
 
         <module>plugin/deleted-messages-vault</module>
         <module>plugin/deleted-messages-vault-cassandra</module>
+        <module>plugin/deleted-messages-vault-postgres</module>
 
         <module>plugin/quota-mailing</module>
         <module>plugin/quota-mailing-cassandra</module>
diff --git a/pom.xml b/pom.xml
index c334da3b23..d36c4280e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -794,6 +794,11 @@
                 
<artifactId>apache-james-mailbox-deleted-messages-vault-cassandra</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
+                
<artifactId>apache-james-mailbox-deleted-messages-vault-postgres</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
                 <artifactId>apache-james-mailbox-event-json</artifactId>


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

Reply via email to